Jelajahi Sumber

Newly needed JCL files

(JclAnsiStrings was needed already before, but until now the few needed function were manually duplicated into the target files)

Source commit: 90fe814e9098ad9611addb3fb404dd45147ed9e6
Martin Prikryl 1 tahun lalu
induk
melakukan
0e0fe9048c
2 mengubah file dengan 6033 tambahan dan 0 penghapusan
  1. 4271 0
      source/packages/jcl/JclAnsiStrings.pas
  2. 1762 0
      source/packages/jcl/JclTD32.pas

+ 4271 - 0
source/packages/jcl/JclAnsiStrings.pas

@@ -0,0 +1,4271 @@
+{**************************************************************************************************}
+{                                                                                                  }
+{ Project JEDI Code Library (JCL)                                                                  }
+{                                                                                                  }
+{ The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); }
+{ you may not use this file except in compliance with the License. You may obtain a copy of the    }
+{ License at http://www.mozilla.org/MPL/                                                           }
+{                                                                                                  }
+{ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF   }
+{ ANY KIND, either express or implied. See the License for the specific language governing rights  }
+{ and limitations under the License.                                                               }
+{                                                                                                  }
+{ The Original Code is JclStrings.pas.                                                             }
+{                                                                                                  }
+{ The Initial Developer of the Original Code is Marcel van Brakel.                                 }
+{ Portions created by Marcel van Brakel are Copyright (C) Marcel van Brakel. All rights reserved.  }
+{                                                                                                  }
+{ Contributor(s):                                                                                  }
+{   Alexander Radchenko                                                                            }
+{   Andreas Hausladen (ahuser)                                                                     }
+{   Anthony Steele                                                                                 }
+{   Azret Botash                                                                                   }
+{   Barry Kelly                                                                                    }
+{   Huanlin Tsai                                                                                   }
+{   Jack N.A. Bakker                                                                               }
+{   Jean-Fabien Connault (cycocrew)                                                                }
+{   John C Molyneux                                                                                }
+{   Leonard Wennekers                                                                              }
+{   Martin Kimmings                                                                                }
+{   Martin Kubecka                                                                                 }
+{   Massimo Maria Ghisalberti                                                                      }
+{   Matthias Thoma (mthoma)                                                                        }
+{   Michael Winter                                                                                 }
+{   Nick Hodges                                                                                    }
+{   Olivier Sannier (obones)                                                                       }
+{   Patrick Kolla                                                                                  }
+{   Pelle F. S. Liljendal                                                                          }
+{   Petr Vones (pvones)                                                                            }
+{   Robert Lee                                                                                     }
+{   Robert Marquardt (marquardt)                                                                   }
+{   Robert Rossmair (rrossmair)                                                                    }
+{   Andreas Schmidt                                                                                }
+{                                                                                                  }
+{**************************************************************************************************}
+{                                                                                                  }
+{ Various character and string routines (searching, testing and transforming)                      }
+{                                                                                                  }
+{**************************************************************************************************}
+{                                                                                                  }
+{ Last modified: $Date::                                                                         $ }
+{ Revision:      $Rev::                                                                          $ }
+{ Author:        $Author::                                                                       $ }
+{                                                                                                  }
+{**************************************************************************************************}
+
+unit JclAnsiStrings;
+
+{$I jcl.inc}
+
+interface
+
+uses
+  {$IFDEF UNITVERSIONING}
+  JclUnitVersioning,
+  {$ENDIF UNITVERSIONING}
+  {$IFDEF HAS_UNITSCOPE}
+  {$IFDEF MSWINDOWS}
+  Winapi.Windows,
+  {$ENDIF MSWINDOWS}
+  System.Classes, System.SysUtils,
+  {$IFDEF HAS_UNIT_ANSISTRINGS}
+  System.AnsiStrings,
+  {$ENDIF HAS_UNIT_ANSISTRINGS}
+  {$ELSE ~HAS_UNITSCOPE}
+  {$IFDEF MSWINDOWS}
+  Windows,
+  {$ENDIF MSWINDOWS}
+  Classes, SysUtils,
+  {$IFDEF HAS_UNIT_ANSISTRINGS}
+  AnsiStrings,
+  {$ENDIF HAS_UNIT_ANSISTRINGS}
+  {$ENDIF ~HAS_UNITSCOPE}
+  JclBase;
+
+// Ansi types
+
+type
+  {$IFDEF SUPPORTS_UNICODE}
+  TJclAnsiStringList = class;
+
+  // Codegear should be the one providing this class, in the AnsiStrings unit.
+  // It has been requested in QC 65630 but this was closed as "won't do".
+  // So we are providing here a very light implementation that is designed
+  // to provide the basics, and in no way be a "copy/paste" of what is in the RTL.
+  TJclAnsiStrings = class(TPersistent)
+  private
+    FDelimiter: AnsiChar;
+    FNameValueSeparator: AnsiChar;
+    FStrictDelimiter: Boolean;
+    FQuoteChar: AnsiChar;
+    FUpdateCount: Integer;
+    function GetText: AnsiString;
+    procedure SetText(const Value: AnsiString);
+    function GetCommaText: AnsiString; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
+    procedure SetCommaText(const Value: AnsiString); {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
+    function GetDelimitedText: AnsiString; overload; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
+    function GetDelimitedText(const ADelimiter: AnsiString; AQuoteChar: AnsiChar): AnsiString; overload;
+    procedure SetDelimitedText(const Value: AnsiString); overload; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
+    procedure SetDelimitedText(const Value, ADelimiter: AnsiString; AQuoteChar: AnsiChar); overload;
+    function ExtractName(const S: AnsiString): AnsiString;
+    function GetName(Index: Integer): AnsiString;
+    function GetValue(const Name: AnsiString): AnsiString;
+    procedure SetValue(const Name, Value: AnsiString);
+    function GetValueFromIndex(Index: Integer): AnsiString;
+    procedure SetValueFromIndex(Index: Integer; const Value: AnsiString);
+  protected
+    procedure AssignTo(Dest: TPersistent); override;
+
+    procedure Error(const Msg: string; Data: Integer); overload;
+    procedure Error(Msg: PResStringRec; Data: Integer); overload;
+
+    function GetString(Index: Integer): AnsiString; virtual; abstract;
+    procedure SetString(Index: Integer; const Value: AnsiString); virtual; abstract;
+    function GetObject(Index: Integer): TObject; virtual; abstract;
+    procedure SetObject(Index: Integer; AObject: TObject); virtual; abstract;
+
+    function GetCapacity: Integer; virtual;
+    procedure SetCapacity(const Value: Integer); virtual;
+    function GetCount: Integer; virtual; abstract;
+    function CompareStrings(const S1, S2: AnsiString): Integer; virtual;
+    procedure SetUpdateState(Updating: Boolean); virtual;
+    property UpdateCount: Integer read FUpdateCount;
+  public
+    constructor Create;
+
+    procedure Assign(Source: TPersistent); override;
+
+    function Add(const S: AnsiString): Integer; virtual;
+    function AddObject(const S: AnsiString; AObject: TObject): Integer; virtual; abstract;
+    procedure AddStrings(Strings: TJclAnsiStrings); virtual;
+    procedure Insert(Index: Integer; const S: AnsiString); virtual;
+    procedure InsertObject(Index: Integer; const S: AnsiString; AObject: TObject); virtual; abstract;
+    procedure Delete(Index: Integer); virtual; abstract;
+    procedure Clear; virtual; abstract;
+    procedure LoadFromFile(const FileName: TFileName); virtual;
+    procedure LoadFromStream(Stream: TStream); virtual;
+    procedure SaveToFile(const FileName: TFileName); virtual;
+    procedure SaveToStream(Stream: TStream); virtual;
+    procedure BeginUpdate;
+    procedure EndUpdate;
+    function IndexOf(const S: AnsiString): Integer; virtual;
+    function IndexOfName(const Name: AnsiString): Integer; virtual;
+    function IndexOfObject(AObject: TObject): Integer; virtual;
+    procedure Exchange(Index1, Index2: Integer); virtual;
+
+    property Delimiter: AnsiChar read FDelimiter write FDelimiter;
+    property DelimitedText: AnsiString read GetDelimitedText write SetDelimitedText;
+    property CommaText: AnsiString read GetCommaText write SetCommaText;
+    property StrictDelimiter: Boolean read FStrictDelimiter write FStrictDelimiter;
+    property QuoteChar: AnsiChar read FQuoteChar write FQuoteChar;
+
+    property Strings[Index: Integer]: AnsiString read GetString write SetString; default;
+    property Objects[Index: Integer]: TObject read GetObject write SetObject;
+    property Text: AnsiString read GetText write SetText;
+    property Count: Integer read GetCount;
+    property Capacity: Integer read GetCapacity write SetCapacity;
+    property Names[Index: Integer]: AnsiString read GetName;
+    property Values[const Name: AnsiString]: AnsiString read GetValue write SetValue;
+    property ValueFromIndex[Index: Integer]: AnsiString read GetValueFromIndex write SetValueFromIndex;
+    property NameValueSeparator: AnsiChar read FNameValueSeparator write FNameValueSeparator;
+  end;
+
+  TJclAnsiStringListSortCompare = function(List: TJclAnsiStringList; Index1, Index2: Integer): Integer;
+
+  TJclAnsiStringObjectHolder = record
+    Str: AnsiString;
+    Obj: TObject;
+  end;
+
+  TJclAnsiStringList = class(TJclAnsiStrings)
+  private
+    FStrings: array of TJclAnsiStringObjectHolder;
+    FCount: Integer;
+    FDuplicates: TDuplicates;
+    FSorted: Boolean;
+    FCaseSensitive: Boolean;
+    FOnChange: TNotifyEvent;
+    FOnChanging: TNotifyEvent;
+    procedure Grow;
+    procedure QuickSort(L, R: Integer; SCompare: TJclAnsiStringListSortCompare);
+    procedure SetSorted(Value: Boolean);
+  protected
+    procedure AssignTo(Dest: TPersistent); override;
+    function GetString(Index: Integer): AnsiString; override;
+    procedure SetString(Index: Integer; const Value: AnsiString); override;
+    function GetObject(Index: Integer): TObject; override;
+    procedure SetObject(Index: Integer; AObject: TObject); override;
+    function GetCapacity: Integer; override;
+    procedure SetCapacity(const Value: Integer); override;
+    function GetCount: Integer; override;
+    function CompareStrings(const S1, S2: AnsiString): Integer; override;
+    procedure SetUpdateState(Updating: Boolean); override;
+
+    procedure Changed; virtual;
+    procedure Changing; virtual;
+  public
+    constructor Create;
+    destructor Destroy; override;
+
+    function AddObject(const S: AnsiString; AObject: TObject): Integer; override;
+    procedure Assign(Source: TPersistent); override;
+    procedure InsertObject(Index: Integer; const S: AnsiString; AObject: TObject); override;
+    procedure Delete(Index: Integer); override;
+    function Find(const S: AnsiString; var Index: Integer): Boolean; virtual;
+    procedure CustomSort(Compare: TJclAnsiStringListSortCompare); virtual;
+    procedure Sort; virtual;
+    procedure Clear; override;
+
+    property CaseSensitive: Boolean read FCaseSensitive write FCaseSensitive;
+    property Duplicates: TDuplicates read FDuplicates write FDuplicates;
+    property Sorted: Boolean read FSorted write SetSorted;
+
+    property OnChange: TNotifyEvent read FOnChange write FOnChange;
+    property OnChanging: TNotifyEvent read FOnChanging write FOnChanging;
+  end;
+  {$ELSE ~SUPPORTS_UNICODE}
+  TJclAnsiStrings = Classes.TStrings;
+  TJclAnsiStringList = Classes.TStringList;
+  {$ENDIF ~SUPPORTS_UNICODE}
+
+  TAnsiStrings = TJclAnsiStrings;
+  TAnsiStringList = TJclAnsiStringList;
+
+// Exceptions
+type
+  EJclAnsiStringError = class(EJclError);
+  EJclAnsiStringListError = class(EJclAnsiStringError);
+
+// Character constants and sets
+
+const
+  // Misc. often used character definitions
+  AnsiNull           = AnsiChar(#0);
+  AnsiSoh            = AnsiChar(#1);
+  AnsiStx            = AnsiChar(#2);
+  AnsiEtx            = AnsiChar(#3);
+  AnsiEot            = AnsiChar(#4);
+  AnsiEnq            = AnsiChar(#5);
+  AnsiAck            = AnsiChar(#6);
+  AnsiBell           = AnsiChar(#7);
+  AnsiBackspace      = AnsiChar(#8);
+  AnsiTab            = AnsiChar(#9);
+  AnsiLineFeed       = AnsiChar(#10);
+  AnsiVerticalTab    = AnsiChar(#11);
+  AnsiFormFeed       = AnsiChar(#12);
+  AnsiCarriageReturn = AnsiChar(#13);
+  AnsiCrLf           = AnsiString(#13#10);
+  AnsiSo             = AnsiChar(#14);
+  AnsiSi             = AnsiChar(#15);
+  AnsiDle            = AnsiChar(#16);
+  AnsiDc1            = AnsiChar(#17);
+  AnsiDc2            = AnsiChar(#18);
+  AnsiDc3            = AnsiChar(#19);
+  AnsiDc4            = AnsiChar(#20);
+  AnsiNak            = AnsiChar(#21);
+  AnsiSyn            = AnsiChar(#22);
+  AnsiEtb            = AnsiChar(#23);
+  AnsiCan            = AnsiChar(#24);
+  AnsiEm             = AnsiChar(#25);
+  AnsiEndOfFile      = AnsiChar(#26);
+  AnsiEscape         = AnsiChar(#27);
+  AnsiFs             = AnsiChar(#28);
+  AnsiGs             = AnsiChar(#29);
+  AnsiRs             = AnsiChar(#30);
+  AnsiUs             = AnsiChar(#31);
+  AnsiSpace          = AnsiChar(' ');
+  AnsiComma          = AnsiChar(',');
+  AnsiBackslash      = AnsiChar('\');
+  AnsiForwardSlash   = AnsiChar('/');
+
+  AnsiDoubleQuote = AnsiChar('"');
+  AnsiSingleQuote = AnsiChar('''');
+
+  {$IFDEF MSWINDOWS}
+  AnsiLineBreak = AnsiCrLf;
+  {$ENDIF MSWINDOWS}
+  {$IFDEF UNIX}
+  AnsiLineBreak = AnsiLineFeed;
+  {$ENDIF UNIX}
+
+  AnsiSignMinus = AnsiChar('-');
+  AnsiSignPlus  = AnsiChar('+');
+
+  // Misc. character sets
+
+  AnsiWhiteSpace             = [AnsiTab, AnsiLineFeed, AnsiVerticalTab,
+    AnsiFormFeed, AnsiCarriageReturn, AnsiSpace];
+  AnsiSigns                  = [AnsiSignMinus, AnsiSignPlus];
+  AnsiUppercaseLetters       = ['A'..'Z'];
+  AnsiLowercaseLetters       = ['a'..'z'];
+  AnsiLetters                = ['A'..'Z', 'a'..'z'];
+  AnsiDecDigits              = ['0'..'9'];
+  AnsiOctDigits              = ['0'..'7'];
+  AnsiHexDigits              = ['0'..'9', 'A'..'F', 'a'..'f'];
+  AnsiValidIdentifierLetters = ['0'..'9', 'A'..'Z', 'a'..'z', '_'];
+
+const
+  // CharType return values
+  C1_UPPER  = $0001; // Uppercase
+  C1_LOWER  = $0002; // Lowercase
+  C1_DIGIT  = $0004; // Decimal digits
+  C1_SPACE  = $0008; // Space characters
+  C1_PUNCT  = $0010; // Punctuation
+  C1_CNTRL  = $0020; // Control characters
+  C1_BLANK  = $0040; // Blank characters
+  C1_XDIGIT = $0080; // Hexadecimal digits
+  C1_ALPHA  = $0100; // Any linguistic character: alphabetic, syllabary, or ideographic
+
+  {$IFDEF MSWINDOWS}
+  {$IFDEF SUPPORTS_EXTSYM}
+  {$EXTERNALSYM C1_UPPER}
+  {$EXTERNALSYM C1_LOWER}
+  {$EXTERNALSYM C1_DIGIT}
+  {$EXTERNALSYM C1_SPACE}
+  {$EXTERNALSYM C1_PUNCT}
+  {$EXTERNALSYM C1_CNTRL}
+  {$EXTERNALSYM C1_BLANK}
+  {$EXTERNALSYM C1_XDIGIT}
+  {$EXTERNALSYM C1_ALPHA}
+  {$ENDIF SUPPORTS_EXTSYM}
+  {$ENDIF MSWINDOWS}
+
+// String Test Routines
+function StrIsAlpha(const S: AnsiString): Boolean;
+function StrIsAlphaNum(const S: AnsiString): Boolean;
+function StrIsAlphaNumUnderscore(const S: AnsiString): Boolean;
+function StrContainsChars(const S: AnsiString; Chars: TSysCharSet; CheckAll: Boolean): Boolean;
+function StrConsistsOfNumberChars(const S: AnsiString): Boolean;
+function StrIsDigit(const S: AnsiString): Boolean;
+function StrIsSubset(const S: AnsiString; const ValidChars: TSysCharSet): Boolean;
+function StrSame(const S1, S2: AnsiString): Boolean;
+
+// String Transformation Routines
+function StrCenter(const S: AnsiString; L: SizeInt; C: AnsiChar = ' '): AnsiString;
+function StrCharPosLower(const S: AnsiString; CharPos: SizeInt): AnsiString;
+function StrCharPosUpper(const S: AnsiString; CharPos: SizeInt): AnsiString;
+function StrDoubleQuote(const S: AnsiString): AnsiString;
+function StrEnsureNoPrefix(const Prefix, Text: AnsiString): AnsiString;
+function StrEnsureNoSuffix(const Suffix, Text: AnsiString): AnsiString;
+function StrEnsurePrefix(const Prefix, Text: AnsiString): AnsiString;
+function StrEnsureSuffix(const Suffix, Text: AnsiString): AnsiString;
+function StrEscapedToString(const S: AnsiString): AnsiString;
+function StrLower(const S: AnsiString): AnsiString;
+procedure StrLowerInPlace(var S: AnsiString);
+procedure StrLowerBuff(S: PAnsiChar);
+procedure StrMove(var Dest: AnsiString; const Source: AnsiString; const ToIndex,
+  FromIndex, Count: SizeInt);
+function StrPadLeft(const S: AnsiString; Len: SizeInt; C: AnsiChar = AnsiSpace): AnsiString;
+function StrPadRight(const S: AnsiString; Len: SizeInt; C: AnsiChar = AnsiSpace): AnsiString;
+function StrProper(const S: AnsiString): AnsiString;
+procedure StrProperBuff(S: PAnsiChar);
+function StrQuote(const S: AnsiString; C: AnsiChar): AnsiString;
+function StrRemoveChars(const S: AnsiString; const Chars: TSysCharSet): AnsiString;
+function StrKeepChars(const S: AnsiString; const Chars: TSysCharSet): AnsiString;
+procedure StrReplace(var S: AnsiString; const Search, Replace: AnsiString; Flags: TReplaceFlags = []);
+function StrReplaceChar(const S: AnsiString; const Source, Replace: AnsiChar): AnsiString;
+function StrReplaceChars(const S: AnsiString; const Chars: TSysCharSet; Replace: AnsiChar): AnsiString;
+function StrReplaceButChars(const S: AnsiString; const Chars: TSysCharSet; Replace: AnsiChar): AnsiString;
+function StrRepeat(const S: AnsiString; Count: SizeInt): AnsiString;
+function StrRepeatLength(const S: AnsiString; const L: SizeInt): AnsiString;
+function StrReverse(const S: AnsiString): AnsiString;
+procedure StrReverseInPlace(var S: AnsiString);
+function StrSingleQuote(const S: AnsiString): AnsiString;
+procedure StrSkipChars(var S: PAnsiChar; const Chars: TSysCharSet); overload;
+procedure StrSkipChars(const S: AnsiString; var Index: SizeInt; const Chars: TSysCharSet); overload;
+function StrSmartCase(const S: AnsiString; Delimiters: TSysCharSet): AnsiString;
+function StrStringToEscaped(const S: AnsiString): AnsiString;
+function StrStripNonNumberChars(const S: AnsiString): AnsiString;
+function StrToHex(const Source: AnsiString): AnsiString;
+function StrTrimCharLeft(const S: AnsiString; C: AnsiChar): AnsiString;
+function StrTrimCharsLeft(const S: AnsiString; const Chars: TSysCharSet): AnsiString;
+function StrTrimCharRight(const S: AnsiString; C: AnsiChar): AnsiString;
+function StrTrimCharsRight(const S: AnsiString; const Chars: TSysCharSet): AnsiString;
+function StrTrimQuotes(const S: AnsiString): AnsiString; overload;
+function StrTrimQuotes(const S: AnsiString; QuoteChar: AnsiChar): AnsiString; overload;
+function StrUpper(const S: AnsiString): AnsiString;
+procedure StrUpperInPlace(var S: AnsiString);
+procedure StrUpperBuff(S: PAnsiChar);
+
+{$IFDEF MSWINDOWS}
+function StrOemToAnsi(const S: AnsiString): AnsiString;
+function StrAnsiToOem(const S: AnsiString): AnsiString;
+{$ENDIF MSWINDOWS}
+
+// String Management
+procedure StrAddRef(var S: AnsiString);
+procedure StrDecRef(var S: AnsiString);
+function StrLength(const S: AnsiString): Longint;
+function StrRefCount(const S: AnsiString): Longint;
+procedure StrResetLength(var S: AnsiString);
+
+// String Search and Replace Routines
+function StrCharCount(const S: AnsiString; C: AnsiChar): SizeInt;
+function StrCharsCount(const S: AnsiString; Chars: TSysCharSet): SizeInt;
+function StrStrCount(const S, SubS: AnsiString): SizeInt;
+function StrCompare(const S1, S2: AnsiString; CaseSensitive: Boolean = False): SizeInt;
+function StrCompareRangeEx(const S1, S2: AnsiString; Index, Count: SizeInt; CaseSensitive: Boolean = False): SizeInt;
+function StrCompareRange(const S1, S2: AnsiString; Index, Count: SizeInt; CaseSensitive: Boolean = True): SizeInt;
+function StrRepeatChar(C: AnsiChar; Count: SizeInt): AnsiString;
+function StrFind(const Substr, S: AnsiString; const Index: SizeInt = 1): SizeInt;
+function StrHasPrefix(const S: AnsiString; const Prefixes: array of AnsiString): Boolean;
+function StrHasSuffix(const S: AnsiString; const Suffixes: array of AnsiString): Boolean;
+function StrIHasPrefix(const S: AnsiString; const Prefixes: array of AnsiString): Boolean;
+function StrIHasSuffix(const S: AnsiString; const Suffixes: array of AnsiString): Boolean;
+function StrIndex(const S: AnsiString; const List: array of AnsiString; CaseSensitive: Boolean = False): SizeInt;
+function StrILastPos(const SubStr, S: AnsiString): SizeInt;
+function StrIPos(const SubStr, S: AnsiString): SizeInt;
+function StrIPrefixIndex(const S: AnsiString; const Prefixes: array of AnsiString): SizeInt;
+function StrIsOneOf(const S: AnsiString; const List: array of AnsiString): Boolean;
+function StrISuffixIndex(const S: AnsiString; const Suffixes: array of AnsiString): SizeInt;
+function StrLastPos(const SubStr, S: AnsiString): SizeInt;
+function StrMatch(const Substr, S: AnsiString; Index: SizeInt = 1): SizeInt;
+function StrMatches(const Substr, S: AnsiString; const Index: SizeInt = 1): Boolean;
+function StrNIPos(const S, SubStr: AnsiString; N: SizeInt): SizeInt;
+function StrNPos(const S, SubStr: AnsiString; N: SizeInt): SizeInt;
+function StrPrefixIndex(const S: AnsiString; const Prefixes: array of AnsiString): SizeInt;
+function StrSearch(const Substr, S: AnsiString; const Index: SizeInt = 1): SizeInt;
+function StrSuffixIndex(const S: AnsiString; const Suffixes: array of AnsiString): SizeInt;
+
+// String Extraction
+// String Extraction
+// Returns the String before SubStr
+function StrAfter(const SubStr, S: AnsiString): AnsiString;
+/// Returns the AnsiString after SubStr
+function StrBefore(const SubStr, S: AnsiString): AnsiString;
+/// Splits a AnsiString at SubStr, returns true when SubStr is found, Left contains the
+/// AnsiString before the SubStr and Rigth the AnsiString behind SubStr
+function StrSplit(const SubStr, S: AnsiString;var Left, Right : AnsiString): boolean;
+/// Returns the AnsiString between Start and Stop
+function StrBetween(const S: AnsiString; const Start, Stop: AnsiChar): AnsiString;
+/// Returns the left N characters of the AnsiString
+function StrChopRight(const S: AnsiString; N: SizeInt): AnsiString;
+/// Returns the left Count characters of the AnsiString
+function StrLeft(const S: AnsiString; Count: SizeInt): AnsiString;
+/// Returns the AnsiString starting from position Start for the Count Characters
+function StrMid(const S: AnsiString; Start, Count: SizeInt): AnsiString;
+/// Returns the AnsiString starting from position N to the end
+function StrRestOf(const S: AnsiString; N: SizeInt): AnsiString;
+/// Returns the right Count characters of the AnsiString
+function StrRight(const S: AnsiString; Count: SizeInt): AnsiString;
+
+// Character Test Routines
+function CharEqualNoCase(const C1, C2: AnsiChar): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
+function CharIsAlpha(const C: AnsiChar): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
+function CharIsAlphaNum(const C: AnsiChar): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
+function CharIsBlank(const C: AnsiChar): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
+function CharIsControl(const C: AnsiChar): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
+function CharIsDelete(const C: AnsiChar): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
+function CharIsDigit(const C: AnsiChar): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
+function CharIsFracDigit(const C: AnsiChar): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
+function CharIsHexDigit(const C: AnsiChar): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
+function CharIsLower(const C: AnsiChar): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
+function CharIsNumberChar(const C: AnsiChar): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
+function CharIsNumber(const C: AnsiChar): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
+function CharIsPrintable(const C: AnsiChar): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
+function CharIsPunctuation(const C: AnsiChar): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
+function CharIsReturn(const C: AnsiChar): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
+function CharIsSpace(const C: AnsiChar): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
+function CharIsUpper(const C: AnsiChar): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
+function CharIsValidIdentifierLetter(const C: AnsiChar): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
+function CharIsWhiteSpace(const C: AnsiChar): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
+function CharIsWildcard(const C: AnsiChar): Boolean; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
+function CharType(const C: AnsiChar): Word;
+
+// Character Transformation Routines
+function CharHex(const C: AnsiChar): Byte;
+function CharLower(const C: AnsiChar): AnsiChar;
+function CharUpper(const C: AnsiChar): AnsiChar;
+function CharToggleCase(const C: AnsiChar): AnsiChar;
+
+// Character Search and Replace
+function CharPos(const S: AnsiString; const C: AnsiChar; const Index: SizeInt = 1): SizeInt;
+function CharLastPos(const S: AnsiString; const C: AnsiChar; const Index: SizeInt = 1): SizeInt;
+function CharIPos(const S: AnsiString; C: AnsiChar; const Index: SizeInt = 1): SizeInt;
+function CharReplace(var S: AnsiString; const Search, Replace: AnsiChar): SizeInt;
+
+// PCharVector
+type
+  PAnsiCharVector = ^PAnsiChar;
+
+function StringsToPCharVector(var Dest: PAnsiCharVector; const Source: TJclAnsiStrings): PAnsiCharVector;
+function PCharVectorCount(Source: PAnsiCharVector): SizeInt;
+procedure PCharVectorToStrings(const Dest: TJclAnsiStrings; Source: PAnsiCharVector);
+procedure FreePCharVector(var Dest: PAnsiCharVector);
+
+// MultiSz Routines
+type
+  PAnsiMultiSz = PAnsiChar;
+
+function StringsToMultiSz(var Dest: PAnsiMultiSz; const Source: TJclAnsiStrings): PAnsiMultiSz;
+procedure MultiSzToStrings(const Dest: TJclAnsiStrings; const Source: PAnsiMultiSz);
+function MultiSzLength(const Source: PAnsiMultiSz): SizeInt;
+procedure AllocateMultiSz(var Dest: PAnsiMultiSz; Len: SizeInt);
+procedure FreeMultiSz(var Dest: PAnsiMultiSz);
+function MultiSzDup(const Source: PAnsiMultiSz): PAnsiMultiSz;
+
+// TJclAnsiStrings Manipulation
+procedure StrIToStrings(S, Sep: AnsiString; const List: TJclAnsiStrings; const AllowEmptyString: Boolean = True);
+procedure StrToStrings(S, Sep: AnsiString; const List: TJclAnsiStrings; const AllowEmptyString: Boolean = True);
+function StringsToStr(const List: TJclAnsiStrings; const Sep: AnsiString; const AllowEmptyString: Boolean = True): AnsiString;
+procedure TrimStrings(const List: TJclAnsiStrings; DeleteIfEmpty: Boolean = True);
+procedure TrimStringsRight(const List: TJclAnsiStrings; DeleteIfEmpty: Boolean = True);
+procedure TrimStringsLeft(const List: TJclAnsiStrings; DeleteIfEmpty: Boolean = True);
+function AddStringToStrings(const S: AnsiString; Strings: TJclAnsiStrings; const Unique: Boolean): Boolean;
+
+// Miscellaneous
+// (OF) moved to JclSysUtils
+//function BooleanToStr(B: Boolean): AnsiString;
+function FileToString(const FileName: TFileName): AnsiString;
+procedure StringToFile(const FileName: TFileName; const Contents: AnsiString; Append: Boolean = False);
+function StrToken(var S: AnsiString; Separator: AnsiChar): AnsiString;
+procedure StrTokens(const S: AnsiString; const List: TJclAnsiStrings);
+procedure StrTokenToStrings(S: AnsiString; Separator: AnsiChar; const List: TJclAnsiStrings);
+function StrWord(const S: AnsiString; var Index: SizeInt; out Word: AnsiString): Boolean; overload;
+function StrWord(var S: PAnsiChar; out Word: AnsiString): Boolean; overload;
+function StrIdent(const S: AnsiString; var Index: SizeInt; out Ident: AnsiString): Boolean; overload;
+function StrIdent(var S: PAnsiChar; out Ident: AnsiString): Boolean; overload;
+function StrToFloatSafe(const S: AnsiString): Float;
+function StrToIntSafe(const S: AnsiString): Integer;
+procedure StrNormIndex(const StrLen: SizeInt; var Index: SizeInt; var Count: SizeInt); overload;
+
+function ArrayOf(List: TJclAnsiStrings): TDynStringArray; overload;
+
+function AnsiCompareNaturalStr(const S1, S2: AnsiString): SizeInt;
+function AnsiCompareNaturalText(const S1, S2: AnsiString): SizeInt;
+
+// Explicit ANSI version of former/deprecated SysUtils PAnsiChar functions
+{$IFNDEF DEPRECATED_SYSUTILS_ANSISTRINGS}
+  {$IFDEF SUPPORTS_INLINE}
+    {$DEFINE ANSI_INLINE} // inline if the functions are in SysUtils but don't force the user to include System.AnsiStrings
+  {$ENDIF SUPPORTS_INLINE}
+{$ENDIF ~DEPRECATED_SYSUTILS_ANSISTRINGS}
+function StrNewA(const Str: PAnsiChar): PAnsiChar;                                           {$IFDEF ANSI_INLINE}inline;{$ENDIF}
+procedure StrDisposeA(Str: PAnsiChar);                                                       {$IFDEF ANSI_INLINE}inline;{$ENDIF}
+
+function StrLenA(S: PAnsiChar): Integer;                                                     {$IFDEF ANSI_INLINE}inline;{$ENDIF}
+function StrEndA(const Str: PAnsiChar): PAnsiChar;                                           {$IFDEF ANSI_INLINE}inline;{$ENDIF}
+function StrPosA(const Str1, Str2: PAnsiChar): PAnsiChar;                                    {$IFDEF ANSI_INLINE}inline;{$ENDIF}
+function StrPasA(const Str: PAnsiChar): AnsiString;                                          {$IFDEF ANSI_INLINE}inline;{$ENDIF}
+function StrCopyA(Dest: PAnsiChar; const Source: PAnsiChar): PAnsiChar;                      {$IFDEF ANSI_INLINE}inline;{$ENDIF}
+function StrLCopyA(Dest: PAnsiChar; const Source: PAnsiChar; MaxLen: Cardinal): PAnsiChar;   {$IFDEF ANSI_INLINE}inline;{$ENDIF}
+function StrPCopyA(Dest: PAnsiChar; const Source: AnsiString): PAnsiChar;                    {$IFDEF ANSI_INLINE}inline;{$ENDIF}
+function StrPLCopyA(Dest: PAnsiChar; const Source: AnsiString; MaxLen: Cardinal): PAnsiChar; {$IFDEF ANSI_INLINE}inline;{$ENDIF}
+function StrECopyA(Dest: PAnsiChar; const Source: PAnsiChar): PAnsiChar;                     {$IFDEF ANSI_INLINE}inline;{$ENDIF}
+function StrCatA(Dest: PAnsiChar; const Source: PAnsiChar): PAnsiChar;                       {$IFDEF ANSI_INLINE}inline;{$ENDIF}
+function StrLCatA(Dest: PAnsiChar; const Source: PAnsiChar; MaxLen: Cardinal): PAnsiChar;    {$IFDEF ANSI_INLINE}inline;{$ENDIF}
+function StrCompA(const Str1, Str2: PAnsiChar): Integer;                                     {$IFDEF ANSI_INLINE}inline;{$ENDIF}
+function StrLCompA(const Str1, Str2: PAnsiChar; MaxLen: Cardinal): Integer;                  {$IFDEF ANSI_INLINE}inline;{$ENDIF}
+function StrICompA(const Str1, Str2: PAnsiChar): Integer;                                    {$IFDEF ANSI_INLINE}inline;{$ENDIF}
+function StrLICompA(const Str1, Str2: PAnsiChar; MaxLen: Cardinal): Integer;                 {$IFDEF ANSI_INLINE}inline;{$ENDIF}
+
+function StrFmtA(Buffer, Format: PAnsiChar; const Args: array of const): PAnsiChar;
+
+function AnsiStrPosA(const Str1, Str2: PAnsiChar): PAnsiChar;                                {$IFDEF ANSI_INLINE}inline;{$ENDIF}
+function AnsiStrLICompA(S1, S2: PAnsiChar; MaxLen: Cardinal): Integer;                       {$IFDEF ANSI_INLINE}inline;{$ENDIF}
+function AnsiStrLCompA(S1, S2: PAnsiChar; MaxLen: Cardinal): Integer;                        {$IFDEF ANSI_INLINE}inline;{$ENDIF}
+
+
+// internal structures published to make function inlining working
+const
+  AnsiCharCount   = Ord(High(AnsiChar)) + 1; // # of chars in one set
+  AnsiLoOffset    = AnsiCharCount * 0;       // offset to lower case chars
+  AnsiUpOffset    = AnsiCharCount * 1;       // offset to upper case chars
+  AnsiReOffset    = AnsiCharCount * 2;       // offset to reverse case chars
+  AnsiCaseMapSize = AnsiCharCount * 3;       // # of chars is a table
+
+var
+  AnsiCaseMap: array [0..AnsiCaseMapSize - 1] of AnsiChar; // case mappings
+  AnsiCaseMapReady: Boolean = False;         // true if case map exists
+  AnsiCharTypes: array [AnsiChar] of Word;
+
+{$IFDEF UNITVERSIONING}
+const
+  UnitVersioning: TUnitVersionInfo = (
+    RCSfile: '$URL$';
+    Revision: '$Revision$';
+    Date: '$Date$';
+    LogPath: 'JCL\source\common';
+    Extra: '';
+    Data: nil
+    );
+{$ENDIF UNITVERSIONING}
+
+implementation
+
+uses
+  {$IFDEF HAS_UNIT_LIBC}
+  Libc,
+  {$ENDIF HAS_UNIT_LIBC}
+  {$IFDEF SUPPORTS_UNICODE}
+  {$IFDEF HAS_UNIT_RTLCONSTS}
+  {$IFDEF HAS_UNITSCOPE}
+  System.RTLConsts,
+  {$ELSE ~HAS_UNITSCOPE}
+  RtlConsts,
+  {$ENDIF}
+  {$ENDIF HAS_UNIT_RTLCONSTS}
+  {$ENDIF SUPPORTS_UNICODE}
+  JclLogic, JclResources, JclStreams, JclSynch, JclSysUtils;
+
+//=== Internal ===============================================================
+
+type
+  TAnsiStrRec = packed record
+    RefCount: Integer;
+    Length: Integer;
+  end;
+  PAnsiStrRec = ^TAnsiStrRec;
+
+const
+  AnsiStrRecSize  = SizeOf(TAnsiStrRec);     // size of the AnsiString header rec
+
+procedure LoadCharTypes;
+var
+  CurrChar: AnsiChar;
+  CurrType: Word;
+begin
+  for CurrChar := Low(AnsiChar) to High(AnsiChar) do
+  begin
+    {$IFDEF MSWINDOWS}
+    CurrType := 0;
+    GetStringTypeExA(LOCALE_USER_DEFAULT, CT_CTYPE1, @CurrChar, SizeOf(AnsiChar), CurrType);
+    {$DEFINE CHAR_TYPES_INITIALIZED}
+    {$ENDIF MSWINDOWS}
+    {$IFDEF LINUX}
+    CurrType := 0;
+    if isupper(Byte(CurrChar)) <> 0 then
+      CurrType := CurrType or C1_UPPER;
+    if islower(Byte(CurrChar)) <> 0 then
+      CurrType := CurrType or C1_LOWER;
+    if isdigit(Byte(CurrChar)) <> 0 then
+      CurrType := CurrType or C1_DIGIT;
+    if isspace(Byte(CurrChar)) <> 0 then
+      CurrType := CurrType or C1_SPACE;
+    if ispunct(Byte(CurrChar)) <> 0 then
+      CurrType := CurrType or C1_PUNCT;
+    if iscntrl(Byte(CurrChar)) <> 0 then
+      CurrType := CurrType or C1_CNTRL;
+    if isblank(Byte(CurrChar)) <> 0 then
+      CurrType := CurrType or C1_BLANK;
+    if isxdigit(Byte(CurrChar)) <> 0 then
+      CurrType := CurrType or C1_XDIGIT;
+    if isalpha(Byte(CurrChar)) <> 0 then
+      CurrType := CurrType or C1_ALPHA;
+    {$DEFINE CHAR_TYPES_INITIALIZED}
+    {$ENDIF LINUX}
+    AnsiCharTypes[CurrChar] := CurrType;
+    {$IFNDEF CHAR_TYPES_INITIALIZED}
+    Implement case map initialization here
+    {$ENDIF ~CHAR_TYPES_INITIALIZED}
+  end;
+end;
+
+procedure LoadCaseMap;
+var
+  CurrChar, UpCaseChar, LoCaseChar, ReCaseChar: AnsiChar;
+begin
+  if not AnsiCaseMapReady then
+  begin
+    for CurrChar := Low(AnsiChar) to High(AnsiChar) do
+    begin
+      {$IFDEF MSWINDOWS}
+      LoCaseChar := CurrChar;
+      UpCaseChar := CurrChar;
+      {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.CharLowerBuffA(@LoCaseChar, 1);
+      {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.CharUpperBuffA(@UpCaseChar, 1);
+      {$DEFINE CASE_MAP_INITIALIZED}
+      {$ENDIF MSWINDOWS}
+      {$IFDEF LINUX}
+      LoCaseChar := AnsiChar(tolower(Byte(CurrChar)));
+      UpCaseChar := AnsiChar(toupper(Byte(CurrChar)));
+      {$DEFINE CASE_MAP_INITIALIZED}
+      {$ENDIF LINUX}
+      {$IFNDEF CASE_MAP_INITIALIZED}
+      Implement case map initialization here
+      {$ENDIF ~CASE_MAP_INITIALIZED}
+      if CharIsUpper(CurrChar) then
+        ReCaseChar := LoCaseChar
+      else
+      if CharIsLower(CurrChar) then
+        ReCaseChar := UpCaseChar
+      else
+        ReCaseChar := CurrChar;
+      AnsiCaseMap[Ord(CurrChar) + AnsiLoOffset] := LoCaseChar;
+      AnsiCaseMap[Ord(CurrChar) + AnsiUpOffset] := UpCaseChar;
+      AnsiCaseMap[Ord(CurrChar) + AnsiReOffset] := ReCaseChar;
+    end;
+    AnsiCaseMapReady := True;
+  end;
+end;
+
+// Uppercases or Lowercases a give AnsiString depending on the
+// passed offset. (UpOffset or LoOffset)
+
+procedure StrCase(var Str: AnsiString; const Offset: SizeInt);
+var
+  P: PAnsiChar;
+  I, L: SizeInt;
+begin
+  if Str <> '' then
+  begin
+    UniqueString(Str);
+    P := PAnsiChar(Str);
+    L := Length(Str);
+    for I := 1 to L do
+    begin
+      P^ := AnsiCaseMap[Offset + Ord(P^)];
+      Inc(P);
+    end;
+  end;
+end;
+
+// Internal utility function
+// Uppercases or Lowercases a give null terminated string depending on the
+// passed offset. (UpOffset or LoOffset)
+
+procedure StrCaseBuff(S: PAnsiChar; const Offset: SizeInt);
+begin
+  if (S <> nil) and (S^ <> #0) then
+  begin
+    repeat
+      S^ := AnsiCaseMap[Offset + Ord(S^)];
+      Inc(S);
+    until S^ = #0;
+  end;
+end;
+
+{$IFDEF SUPPORTS_UNICODE}
+
+//=== { TJclAnsiStrings } ====================================================
+
+constructor TJclAnsiStrings.Create;
+begin
+  inherited Create;
+
+  FDelimiter := ',';
+  FNameValueSeparator := '=';
+  FQuoteChar := '"';
+  FStrictDelimiter := False;
+end;
+
+procedure TJclAnsiStrings.Assign(Source: TPersistent);
+var
+  StringsSource: TStrings;
+  I: Integer;
+begin
+  if Source is TStrings then
+  begin
+    StringsSource := TStrings(Source);
+    BeginUpdate;
+    try
+      Clear;
+      FDelimiter := AnsiChar(StringsSource.Delimiter);
+      FNameValueSeparator := AnsiChar(StringsSource.NameValueSeparator);
+      for I := 0 to StringsSource.Count - 1 do
+        AddObject(AnsiString(StringsSource.Strings[I]), StringsSource.Objects[I]);
+    finally
+      EndUpdate;
+    end;
+  end
+  else
+    inherited Assign(Source);
+end;
+
+procedure TJclAnsiStrings.AssignTo(Dest: TPersistent);
+var
+  StringsDest: TStrings;
+  AnsiStringsDest: TJclAnsiStrings;
+  I: Integer;
+begin
+  if Dest is TStrings then
+  begin
+    StringsDest := TStrings(Dest);
+    StringsDest.BeginUpdate;
+    try
+      StringsDest.Clear;
+      StringsDest.Delimiter := Char(Delimiter);
+      StringsDest.NameValueSeparator := Char(NameValueSeparator);
+      for I := 0 to Count - 1 do
+        StringsDest.AddObject(string(Strings[I]), Objects[I]);
+    finally
+      StringsDest.EndUpdate;
+    end;
+  end
+  else
+  if Dest is TJclAnsiStrings then
+  begin
+    AnsiStringsDest := TJclAnsiStrings(Dest);
+    AnsiStringsDest.BeginUpdate;
+    try
+      AnsiStringsDest.Clear;
+      AnsiStringsDest.FNameValueSeparator := FNameValueSeparator;
+      AnsiStringsDest.FDelimiter := FDelimiter;
+      for I := 0 to Count - 1 do
+        AnsiStringsDest.AddObject(Strings[I], Objects[I]);
+    finally
+      AnsiStringsDest.EndUpdate;
+    end;
+  end
+  else
+    inherited AssignTo(Dest);
+end;
+
+function TJclAnsiStrings.Add(const S: AnsiString): Integer;
+begin
+  Result := AddObject(S, nil);
+end;
+
+procedure TJclAnsiStrings.AddStrings(Strings: TJclAnsiStrings);
+var
+  I: Integer;
+begin
+  for I := 0 to Strings.Count - 1 do
+    Add(Strings.Strings[I]);
+end;
+
+procedure TJclAnsiStrings.Error(const Msg: string; Data: Integer);
+begin
+  raise EJclAnsiStringListError.CreateFmt(Msg, [Data]);
+end;
+
+procedure TJclAnsiStrings.Error(Msg: PResStringRec; Data: Integer);
+begin
+  Error(LoadResString(Msg), Data);
+end;
+
+function TJclAnsiStrings.CompareStrings(const S1, S2: AnsiString): Integer;
+begin
+  Result := CompareStr(S1, S2);
+end;
+
+procedure TJclAnsiStrings.SetUpdateState(Updating: Boolean);
+begin
+end;
+
+function TJclAnsiStrings.IndexOf(const S: AnsiString): Integer;
+begin
+  for Result := 0 to Count - 1 do
+    if CompareStrings(Strings[Result], S) = 0 then
+      Exit;
+  Result := -1;
+end;
+
+function TJclAnsiStrings.IndexOfName(const Name: AnsiString): Integer;
+var
+  P: Integer;
+  S: AnsiString;
+begin
+  for Result := 0 to Count - 1 do
+  begin
+    S := Strings[Result];
+    P := AnsiPos(NameValueSeparator, S);
+    if (P > 0) and (CompareStrings(Copy(S, 1, P - 1), Name) = 0) then
+      Exit;
+  end;
+  Result := -1;
+end;
+
+function TJclAnsiStrings.IndexOfObject(AObject: TObject): Integer;
+begin
+  for Result := 0 to Count - 1 do
+    if Objects[Result] = AObject then
+      Exit;
+  Result := -1;
+end;
+
+procedure TJclAnsiStrings.Exchange(Index1, Index2: Integer);
+var
+  TempString: AnsiString;
+  TempObject: TObject;
+begin
+  BeginUpdate;
+  try
+    TempString := Strings[Index1];
+    TempObject := Objects[Index1];
+    Strings[Index1] := Strings[Index2];
+    Objects[Index1] := Objects[Index2];
+    Strings[Index2] := TempString;
+    Objects[Index2] := TempObject;
+  finally
+    EndUpdate;
+  end;
+end;
+
+function TJclAnsiStrings.GetCommaText: AnsiString;
+begin
+  Result := GetDelimitedText(AnsiComma, AnsiDoubleQuote);
+end;
+
+function TJclAnsiStrings.GetDelimitedText: AnsiString;
+begin
+  Result := GetDelimitedText(Delimiter, QuoteChar);
+end;
+
+function TJclAnsiStrings.GetDelimitedText(const ADelimiter: AnsiString; AQuoteChar: AnsiChar): AnsiString;
+
+  function Quoted(Item: AnsiString): AnsiString;
+  begin
+    if (not StrictDelimiter) and ((Pos(AnsiSpace, Item) > 0) or (Pos(FQuoteChar, Item) > 0)) then
+    begin
+      Result := AnsiQuotedStr(Item, AQuoteChar);
+    end
+    else
+      Result := Item;
+  end;
+
+var
+  I: Integer;
+begin
+  Result := '';
+  for I := 0 to Count - 2 do
+    Result := Result + Quoted(Strings[I]) + ADelimiter;
+  if Count > 0 then
+    Result := Result + Quoted(Strings[Count - 1]);
+end;
+
+procedure TJclAnsiStrings.Insert(Index: Integer; const S: AnsiString);
+begin
+  InsertObject(Index, S, nil);
+end;
+
+procedure TJclAnsiStrings.SetCommaText(const Value: AnsiString);
+begin
+  SetDelimitedText(Value, AnsiComma, AnsiDoubleQuote);
+end;
+
+procedure TJclAnsiStrings.SetDelimitedText(const Value: AnsiString);
+begin
+  SetDelimitedText(Value, Delimiter, QuoteChar);
+end;
+
+procedure TJclAnsiStrings.SetDelimitedText(const Value, ADelimiter: AnsiString; AQuoteChar: AnsiChar);
+
+  procedure InternalAdd(Item: AnsiString);
+  begin
+    Item := StrTrimQuotes(Item, AQuoteChar);
+    StrReplace(Item, AQuoteChar + AQuoteChar, AQuoteChar, [rfReplaceAll]);
+    Add(Item);
+  end;
+
+var
+  ValueLength, LastStart, Index, QuoteCharCount: Integer;
+  ValueChar: AnsiChar;
+begin
+  Clear;
+  LastStart := 1;
+  QuoteCharCount := 0;
+  ValueLength := Length(Value);
+  for Index := 1 to ValueLength do
+  begin
+    ValueChar := Value[Index];
+    if ValueChar = AQuoteChar then
+      Inc(QuoteCharCount);
+    if ((ValueChar = ADelimiter) or ((ValueChar = ' ') and (not StrictDelimiter)))
+    and ((not Odd(QuoteCharCount) or (QuoteCharCount = 0))) then
+    begin
+      if StrictDelimiter then
+        Add(Copy(Value, LastStart, Index - LastStart))
+      else
+        InternalAdd(Copy(Value, LastStart, Index - LastStart));
+      QuoteCharCount := 0;
+      LastStart := Index + 1;
+    end;
+    if (Index = ValueLength) and (LastStart <= ValueLength) then
+    begin
+      if StrictDelimiter then
+        Add(Copy(Value, LastStart, ValueLength - LastStart + 1))
+      else
+        InternalAdd(Copy(Value, LastStart, Index - LastStart + 1));
+    end;
+  end;
+end;
+
+function TJclAnsiStrings.GetText: AnsiString;
+var
+  I: Integer;
+begin
+  Result := '';
+  for I := 0 to Count - 2 do
+    Result := Result + Strings[I] + AnsiLineBreak;
+  if Count > 0 then
+    Result := Result + Strings[Count - 1] + AnsiLineBreak;
+end;
+
+procedure TJclAnsiStrings.SetText(const Value: AnsiString);
+var
+  Index, Start, Len: Integer;
+  S: AnsiString;
+begin
+  Clear;
+  Len := Length(Value);
+  Index := 1;
+  while Index <= Len do
+  begin
+    Start := Index;
+    while (Index <= Len) and not CharIsReturn(Value[Index]) do
+      Inc(Index);
+
+    S := Copy(Value, Start, Index - Start);
+    Add(S);
+
+    if (Index <= Len) and (Value[Index] = AnsiCarriageReturn) then
+      Inc(Index);
+    if (Index <= Len) and (Value[Index] = AnsiLineFeed) then
+      Inc(Index);
+  end;
+end;
+
+function TJclAnsiStrings.GetCapacity: Integer;
+begin
+  Result := Count; // Might be overridden in derived classes
+end;
+
+procedure TJclAnsiStrings.SetCapacity(const Value: Integer);
+begin
+  // Nothing at this level
+end;
+
+procedure TJclAnsiStrings.BeginUpdate;
+begin
+  if FUpdateCount = 0 then SetUpdateState(True);
+  Inc(FUpdateCount);
+end;
+
+procedure TJclAnsiStrings.EndUpdate;
+begin
+  Dec(FUpdateCount);
+  if FUpdateCount = 0 then SetUpdateState(False);
+end;
+
+procedure TJclAnsiStrings.LoadFromFile(const FileName: TFileName);
+var
+  Stream: TStream;
+begin
+  Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
+  try
+    LoadFromStream(Stream);
+  finally
+    Stream.Free;
+  end;
+end;
+
+procedure TJclAnsiStrings.LoadFromStream(Stream: TStream);
+var
+  Size: Integer;
+  S: AnsiString;
+begin
+  BeginUpdate;
+  try
+    Size := Stream.Size - Stream.Position;
+    System.SetString(S, nil, Size);
+    Stream.Read(PAnsiChar(S)^, Size);
+    SetText(S);
+  finally
+    EndUpdate;
+  end;
+end;
+
+procedure TJclAnsiStrings.SaveToFile(const FileName: TFileName);
+var
+  Stream: TStream;
+begin
+  Stream := TFileStream.Create(FileName, fmCreate);
+  try
+    SaveToStream(Stream);
+  finally
+    Stream.Free;
+  end;
+end;
+
+procedure TJclAnsiStrings.SaveToStream(Stream: TStream);
+var
+  S: AnsiString;
+begin
+  S := GetText;
+  Stream.WriteBuffer(PAnsiChar(S)^, Length(S));
+end;
+
+function TJclAnsiStrings.ExtractName(const S: AnsiString): AnsiString;
+var
+  P: Integer;
+begin
+  Result := S;
+  P := AnsiPos(NameValueSeparator, Result);
+  if P > 0 then
+    SetLength(Result, P - 1)
+  else
+    SetLength(Result, 0);
+end;
+
+function TJclAnsiStrings.GetName(Index: Integer): AnsiString;
+begin
+  Result := ExtractName(Strings[Index]);
+end;
+
+function TJclAnsiStrings.GetValue(const Name: AnsiString): AnsiString;
+var
+  I: Integer;
+begin
+  I := IndexOfName(Name);
+  if I >= 0 then
+    Result := Copy(GetString(I), Length(Name) + 2, MaxInt)
+  else
+    Result := '';
+end;
+
+procedure TJclAnsiStrings.SetValue(const Name, Value: AnsiString);
+var
+  I: Integer;
+begin
+  I := IndexOfName(Name);
+  if Value <> '' then
+  begin
+    if I < 0 then
+      I := Add('');
+    SetString(I, Name + NameValueSeparator + Value);
+  end
+  else
+  begin
+    if I >= 0 then
+      Delete(I);
+  end;
+end;
+
+function TJclAnsiStrings.GetValueFromIndex(Index: Integer): AnsiString;
+var
+  S: AnsiString;
+  P: Integer;
+begin
+  if Index >= 0 then
+  begin
+    S := Strings[Index];
+    P := AnsiPos(NameValueSeparator, S);
+    if P > 0 then
+      Result := Copy(S, P + 1, Length(S) - P)
+    else
+      Result := '';
+  end
+  else
+    Result := '';
+end;
+
+procedure TJclAnsiStrings.SetValueFromIndex(Index: Integer; const Value: AnsiString);
+begin
+  if Value <> '' then
+  begin
+    if Index < 0 then
+      Index := Add('');
+    SetString(Index, Names[Index] + NameValueSeparator + Value);
+  end
+  else
+  begin
+    if Index >= 0 then
+      Delete(Index);
+  end;
+end;
+
+//=== { TJclAnsiStringList } =================================================
+
+constructor TJclAnsiStringList.Create;
+begin
+  inherited Create;
+  FCaseSensitive := True;
+end;
+
+destructor TJclAnsiStringList.Destroy;
+begin
+  FOnChange := nil;
+  FOnChanging := nil;
+
+  inherited Destroy;
+end;
+
+procedure TJclAnsiStringList.Assign(Source: TPersistent);
+var
+  StringListSource: TStringList;
+begin
+  if Source is TStringList then
+  begin
+    StringListSource := TStringList(Source);
+    FDuplicates := StringListSource.Duplicates;
+    FSorted := StringListSource.Sorted;
+    FCaseSensitive := StringListSource.CaseSensitive;
+  end;
+  inherited Assign(Source);
+end;
+
+procedure TJclAnsiStringList.AssignTo(Dest: TPersistent);
+var
+  StringListDest: TStringList;
+  AnsiStringListDest: TJclAnsiStringList;
+begin
+  if Dest is TStringList then
+  begin
+    StringListDest := TStringList(Dest);
+    StringListDest.Clear; // make following assignments a lot faster
+    StringListDest.Duplicates := FDuplicates;
+    StringListDest.Sorted := FSorted;
+    StringListDest.CaseSensitive := FCaseSensitive;
+  end
+  else
+  if Dest is TJclAnsiStringList then
+  begin
+    AnsiStringListDest := TJclAnsiStringList(Dest);
+    AnsiStringListDest.Clear;
+    AnsiStringListDest.FDuplicates := FDuplicates;
+    AnsiStringListDest.FSorted := FSorted;
+    AnsiStringListDest.FCaseSensitive := FCaseSensitive;
+  end;
+  inherited AssignTo(Dest);
+end;
+
+function TJclAnsiStringList.CompareStrings(const S1: AnsiString; const S2: AnsiString): Integer;
+begin
+  if FCaseSensitive then
+    Result := CompareStr(S1, S2)
+  else
+    Result := CompareText(S1, S2);
+end;
+
+procedure TJclAnsiStringList.SetUpdateState(Updating: Boolean);
+begin
+  if Updating then Changing else Changed;
+end;
+
+procedure TJclAnsiStringList.Changed;
+begin
+  if (FUpdateCount = 0) and Assigned(FOnChange) then
+    FOnChange(Self);
+end;
+
+procedure TJclAnsiStringList.Changing;
+begin
+  if (FUpdateCount = 0) and Assigned(FOnChanging) then
+    FOnChanging(Self);
+end;
+
+procedure TJclAnsiStringList.Grow;
+var
+  Delta: Integer;
+begin
+  if Capacity > 64 then
+    Delta := Capacity div 4
+  else if Capacity > 8 then
+    Delta := 16
+  else
+    Delta := 4;
+
+  SetCapacity(Capacity + Delta);
+end;
+
+function TJclAnsiStringList.GetString(Index: Integer): AnsiString;
+begin
+  if (Index < 0) or (Index >= FCount) then
+    Error(@SListIndexError, Index);
+
+  Result := FStrings[Index].Str;
+end;
+
+procedure TJclAnsiStringList.SetString(Index: Integer; const Value: AnsiString);
+begin
+  if Sorted then
+    Error(@SSortedListError, 0);
+
+  if (Index < 0) or (Index >= FCount) then
+    Error(@SListIndexError, Index);
+
+  FStrings[Index].Str := Value;
+end;
+
+function TJclAnsiStringList.GetObject(Index: Integer): TObject;
+begin
+  if (Index < 0) or (Index >= FCount) then
+    Error(@SListIndexError, Index);
+
+  Result := FStrings[Index].Obj;
+end;
+
+procedure TJclAnsiStringList.SetObject(Index: Integer; AObject: TObject);
+begin
+  if (Index < 0) or (Index >= FCount) then
+    Error(@SListIndexError, Index);
+
+  FStrings[Index].Obj := AObject;
+end;
+
+function TJclAnsiStringList.GetCapacity: Integer;
+begin
+  Result := Length(FStrings);
+end;
+
+procedure TJclAnsiStringList.SetCapacity(const Value: Integer);
+begin
+  if (Value < FCount) then
+    Error(@SListCapacityError, Value);
+
+  if Value <> Capacity then
+    SetLength(FStrings, Value);
+end;
+
+function TJclAnsiStringList.GetCount: Integer;
+begin
+  Result := FCount;
+end;
+
+procedure TJclAnsiStringList.InsertObject(Index: Integer; const S: AnsiString; AObject: TObject);
+var
+  I: Integer;
+begin
+  if Count = Capacity then
+    Grow;
+
+  for I := Count - 1 downto Index do
+    FStrings[I + 1] := FStrings[I];
+
+  FStrings[Index].Str := S;
+  FStrings[Index].Obj := AObject;
+  Inc(FCount);
+end;
+
+function TJclAnsiStringList.AddObject(const S: AnsiString; AObject: TObject): Integer;
+var
+  Found: Boolean;
+begin
+  if not Sorted then
+    Result := Count
+  else
+  begin
+    Found := Find(S, Result);
+    case Duplicates of
+      dupAccept: ;
+      dupIgnore:
+        if Found then
+          Exit;
+      dupError:
+        if Found then
+          Error(@SDuplicateString, 0);
+    end;
+  end;
+
+  InsertObject(Result, S, AObject);
+end;
+
+procedure TJclAnsiStringList.Delete(Index: Integer);
+var
+  I: Integer;
+begin
+  if (Index < 0) or (Index >= FCount) then
+    Error(@SListIndexError, Index);
+
+  for I := Index to Count - 2 do
+    FStrings[I] := FStrings[I + 1];
+    
+  FStrings[FCount - 1].Str := '';  // the last string is no longer useful
+    
+  Dec(FCount);
+end;
+
+procedure TJclAnsiStringList.Clear;
+var
+  I: Integer;
+begin
+  FCount := 0;
+  for I := 0 to Length(FStrings) - 1 do
+  begin
+    FStrings[I].Str := '';
+    FStrings[I].Obj := nil;
+  end;
+end;
+
+function TJclAnsiStringList.Find(const S: AnsiString; var Index: Integer): Boolean;
+var
+  L, H, I, C: Integer;
+begin
+  Result := False;
+  L := 0;
+  H := FCount - 1;
+  while L <= H do
+  begin
+    I := (L + H) shr 1;
+    C := CompareStrings(FStrings[I].Str, S);
+    if C < 0 then
+      L := I + 1
+    else
+    begin
+      H := I - 1;
+      if C = 0 then
+      begin
+        Result := True;
+        if Duplicates <> dupAccept then
+          L := I;
+      end;
+    end;
+  end;
+  Index := L;
+end;
+
+function AnsiStringListCompareStrings(List: TJclAnsiStringList; Index1, Index2: Integer): Integer;
+begin
+  Result := List.CompareStrings(List.FStrings[Index1].Str,
+                                List.FStrings[Index2].Str);
+end;
+
+procedure TJclAnsiStringList.Sort;
+begin
+  CustomSort(AnsiStringListCompareStrings);
+end;
+
+procedure TJclAnsiStringList.CustomSort(Compare: TJclAnsiStringListSortCompare);
+begin
+  if not Sorted and (FCount > 1) then
+    QuickSort(0, FCount - 1, Compare);
+end;
+
+procedure TJclAnsiStringList.QuickSort(L, R: Integer; SCompare: TJclAnsiStringListSortCompare);
+var
+  I, J, P: Integer;
+begin
+  repeat
+    I := L;
+    J := R;
+    P := (L + R) shr 1;
+    repeat
+      while SCompare(Self, I, P) < 0 do
+        Inc(I);
+      while SCompare(Self, J, P) > 0 do
+        Dec(J);
+      if I <= J then
+      begin
+        if I <> J then
+          Exchange(I, J);
+        if P = I then
+          P := J
+        else
+        if P = J then
+          P := I;
+        Inc(I);
+        Dec(J);
+      end;
+    until I > J;
+    if L < J then
+      QuickSort(L, J, SCompare);
+    L := I;
+  until I >= R;
+end;
+
+procedure TJclAnsiStringList.SetSorted(Value: Boolean);
+begin
+  if FSorted <> Value then
+  begin
+    if Value then
+      Sort;
+    FSorted := Value;
+  end;
+end;
+
+{$ENDIF SUPPORTS_UNICODE}
+
+// String Test Routines
+function StrIsAlpha(const S: AnsiString): Boolean;
+var
+  I: SizeInt;
+begin
+  Result := S <> '';
+  for I := 1 to Length(S) do
+  begin
+    if not CharIsAlpha(S[I]) then
+    begin
+      Result := False;
+      Exit;
+    end;
+  end;
+end;
+
+function StrIsAlphaNum(const S: AnsiString): Boolean;
+var
+  I: SizeInt;
+begin
+  Result := S <> '';
+  for I := 1 to Length(S) do
+  begin
+    if not CharIsAlphaNum(S[I]) then
+    begin
+      Result := False;
+      Exit;
+    end;
+  end;
+end;
+
+function StrConsistsofNumberChars(const S: AnsiString): Boolean;
+var
+  I: SizeInt;
+begin
+  Result := S <> '';
+  for I := 1 to Length(S) do
+  begin
+    if not CharIsNumberChar(S[I]) then
+    begin
+      Result := False;
+      Exit;
+    end;
+  end;
+end;
+
+function StrContainsChars(const S: AnsiString; Chars: TSysCharSet; CheckAll: Boolean): Boolean;
+var
+  I: SizeInt;
+  C: AnsiChar;
+begin
+  Result := Chars = [];
+  if not Result then
+  begin
+    if CheckAll then
+    begin
+      for I := 1 to Length(S) do
+      begin
+        C := S[I];
+        if C in Chars then
+        begin
+          Chars := Chars - [C];
+          if Chars = [] then
+            Break;
+        end;
+      end;
+      Result := (Chars = []);
+    end
+    else
+    begin
+      for I := 1 to Length(S) do
+        if S[I] in Chars then
+        begin
+          Result := True;
+          Break;
+        end;
+    end;
+  end;
+end;
+
+function StrIsAlphaNumUnderscore(const S: AnsiString): Boolean;
+var
+  I: SizeInt;
+  C: AnsiChar;
+begin
+  for i := 1 to Length(s) do
+  begin
+    C := S[I];
+
+    if not (CharIsAlphaNum(C) or (C = '_')) then
+    begin
+      Result := False;
+      Exit;
+    end;
+  end;
+
+  Result := True and (Length(S) > 0);
+end;
+
+function StrIsDigit(const S: AnsiString): Boolean;
+var
+  I: SizeInt;
+begin
+  Result := S <> '';
+  for I := 1 to Length(S) do
+  begin
+    if not CharIsDigit(S[I]) then
+    begin
+      Result := False;
+      Exit;
+    end;
+  end;
+end;
+
+function StrIsSubset(const S: AnsiString; const ValidChars: TSysCharSet): Boolean;
+var
+  I: SizeInt;
+begin
+  for I := 1 to Length(S) do
+  begin
+    if not (S[I] in ValidChars) then
+    begin
+      Result := False;
+      Exit;
+    end;
+  end;
+
+  Result := True and (Length(S) > 0);
+end;
+
+function StrSame(const S1, S2: AnsiString): Boolean;
+begin
+  Result := StrCompare(S1, S2) = 0;
+end;
+
+//=== String Transformation Routines =========================================
+
+function StrCenter(const S: AnsiString; L: SizeInt; C: AnsiChar = ' '): AnsiString;
+begin
+  if Length(S) < L then
+  begin
+    Result := StringOfChar(C, (L - Length(S)) div 2) + S;
+    Result := Result + StringOfChar(C, L - Length(Result));
+  end
+  else
+    Result := S;
+end;
+
+function StrCharPosLower(const S: AnsiString; CharPos: SizeInt): AnsiString;
+begin
+  Result := S;
+  if (CharPos > 0) and (CharPos <= Length(S)) then
+    Result[CharPos] := CharLower(Result[CharPos]);
+end;
+
+function StrCharPosUpper(const S: AnsiString; CharPos: SizeInt): AnsiString;
+begin
+  Result := S;
+  if (CharPos > 0) and (CharPos <= Length(S)) then
+    Result[CharPos] := CharUpper(Result[CharPos]);
+end;
+
+function StrDoubleQuote(const S: AnsiString): AnsiString;
+begin
+  Result := AnsiDoubleQuote + S + AnsiDoubleQuote;
+end;
+
+function StrEnsureNoPrefix(const Prefix, Text: AnsiString): AnsiString;
+var
+  PrefixLen: SizeInt;
+begin
+  PrefixLen := Length(Prefix);
+  if Copy(Text, 1, PrefixLen) = Prefix then
+    Result := Copy(Text, PrefixLen + 1, Length(Text))
+  else
+    Result := Text;
+end;
+
+function StrEnsureNoSuffix(const Suffix, Text: AnsiString): AnsiString;
+var
+  SuffixLen: SizeInt;
+  StrLength: SizeInt;
+begin
+  SuffixLen := Length(Suffix);
+  StrLength := Length(Text);
+  if Copy(Text, StrLength - SuffixLen + 1, SuffixLen) = Suffix then
+    Result := Copy(Text, 1, StrLength - SuffixLen)
+  else
+    Result := Text;
+end;
+
+function StrEnsurePrefix(const Prefix, Text: AnsiString): AnsiString;
+var
+  PrefixLen: SizeInt;
+begin
+  PrefixLen := Length(Prefix);
+  if Copy(Text, 1, PrefixLen) = Prefix then
+    Result := Text
+  else
+    Result := Prefix + Text;
+end;
+
+function StrEnsureSuffix(const Suffix, Text: AnsiString): AnsiString;
+var
+  SuffixLen: SizeInt;
+begin
+  SuffixLen := Length(Suffix);
+  if Copy(Text, Length(Text) - SuffixLen + 1, SuffixLen) = Suffix then
+    Result := Text
+  else
+    Result := Text + Suffix;
+end;
+
+function StrEscapedToString(const S: AnsiString): AnsiString;
+  procedure HandleHexEscapeSeq(const S: AnsiString; var I: SizeInt; Len: SizeInt; var Dest: AnsiString);
+  const
+    HexDigits = AnsiString('0123456789abcdefABCDEF');
+  var
+    StartI, Val, N: SizeInt;
+  begin
+    StartI := I;
+    N := Pos(S[I + 1], HexDigits) - 1;
+    if N < 0 then
+      // '\x' without hex digit following is not escape sequence
+      Dest := Dest + '\x'
+    else
+    begin
+      Inc(I); // Jump over x
+      if N >= 16 then
+        N := N - 6;
+      Val := N;
+      // Same for second digit
+      if I < Len then
+      begin
+        N := Pos(S[I + 1], HexDigits) - 1;
+        if N >= 0 then
+        begin
+          Inc(I); // Jump over first digit
+          if N >= 16 then
+            N := N - 6;
+          Val := Val * 16 + N;
+        end;
+      end;
+
+      if Val > Ord(High(AnsiChar)) then
+        raise EJclAnsiStringError.CreateResFmt(@RsNumericConstantTooLarge, [Val, StartI]);
+
+      Dest := Dest + AnsiChar(Val);
+    end;
+  end;
+
+  procedure HandleOctEscapeSeq(const S: AnsiString; var I: SizeInt; Len: SizeInt; var Dest: AnsiString);
+  const
+    OctDigits = AnsiString('01234567');
+  var
+    StartI, Val, N: SizeInt;
+  begin
+    StartI := I;
+    // first digit
+    Val := Pos(S[I], OctDigits) - 1;
+    if I < Len then
+    begin
+      N := Pos(S[I + 1], OctDigits) - 1;
+      if N >= 0 then
+      begin
+        Inc(I);
+        Val := Val * 8 + N;
+      end;
+      if I < Len then
+      begin
+        N := Pos(S[I + 1], OctDigits) - 1;
+        if N >= 0 then
+        begin
+          Inc(I);
+          Val := Val * 8 + N;
+        end;
+      end;
+    end;
+
+    if Val > Ord(High(AnsiChar)) then
+      raise EJclAnsiStringError.CreateResFmt(@RsNumericConstantTooLarge, [Val, StartI]);
+
+    Dest := Dest + AnsiChar(Val);
+  end;
+
+var
+  I, Len: SizeInt;
+begin
+  Result := '';
+  I := 1;
+  Len := Length(S);
+  while I <= Len do
+  begin
+    if not ((S[I] = '\') and (I < Len)) then
+      Result := Result + S[I]
+    else
+    begin
+      Inc(I); // Jump over escape character
+      case S[I] of
+        'a':
+          Result := Result + AnsiBell;
+        'b':
+          Result := Result + AnsiBackspace;
+        'f':
+          Result := Result + AnsiFormFeed;
+        'n':
+          Result := Result + AnsiLineFeed;
+        'r':
+          Result := Result + AnsiCarriageReturn;
+        't':
+          Result := Result + AnsiTab;
+        'v':
+          Result := Result + AnsiVerticalTab;
+        '\':
+          Result := Result + '\';
+        '"':
+          Result := Result + '"';
+        '''':
+          Result := Result + ''''; // Optionally escaped
+        '?':
+          Result := Result + '?';  // Optionally escaped
+        'x':
+          if I < Len then
+            // Start of hex escape sequence
+            HandleHexEscapeSeq(S, I, Len, Result)
+          else
+            // '\x' at end of AnsiString is not escape sequence
+            Result := Result + '\x';
+        '0'..'7':
+          // start of octal escape sequence
+          HandleOctEscapeSeq(S, I, Len, Result);
+      else
+        // no escape sequence
+        Result := Result + '\' + S[I];
+      end;
+    end;
+    Inc(I);
+  end;
+end;
+
+function StrLower(const S: AnsiString): AnsiString;
+begin
+  Result := S;
+  StrLowerInPlace(Result);
+end;
+
+procedure StrLowerInPlace(var S: AnsiString);
+begin
+  StrCase(S, AnsiLoOffset);
+end;
+
+procedure StrLowerBuff(S: PAnsiChar);
+begin
+  StrCaseBuff(S, AnsiLoOffset);
+end;
+
+procedure StrMove(var Dest: AnsiString; const Source: AnsiString;
+  const ToIndex, FromIndex, Count: SizeInt);
+begin
+  // Check strings
+  if (Source = '') or (Length(Dest) = 0) then
+    Exit;
+
+  // Check FromIndex
+  if (FromIndex <= 0) or (FromIndex > Length(Source)) or
+    (ToIndex <= 0) or (ToIndex > Length(Dest)) or
+    ((FromIndex + Count - 1) > Length(Source)) or ((ToIndex + Count - 1) > Length(Dest)) then
+    { TODO : Is failure without notice the proper thing to do here? }
+    Exit;
+
+  // Move
+  Move(Source[FromIndex], Dest[ToIndex], Count);
+end;
+
+function StrPadLeft(const S: AnsiString; Len: SizeInt; C: AnsiChar): AnsiString;
+var
+  L: SizeInt;
+begin
+  L := Length(S);
+  if L < Len then
+    Result := StringOfChar(C, Len - L) + S
+  else
+    Result := S;
+end;
+
+function StrPadRight(const S: AnsiString; Len: SizeInt; C: AnsiChar): AnsiString;
+var
+  L: SizeInt;
+begin
+  L := Length(S);
+  if L < Len then
+    Result := S + StringOfChar(C, Len - L)
+  else
+    Result := S;
+end;
+
+function StrProper(const S: AnsiString): AnsiString;
+begin
+  Result := StrLower(S);
+  if Result <> '' then
+    Result[1] := UpCase(Result[1]);
+end;
+
+procedure StrProperBuff(S: PAnsiChar);
+begin
+  if (S <> nil) and (S^ <> #0) then
+  begin
+    StrLowerBuff(S);
+    S^ := CharUpper(S^);
+  end;
+end;
+
+function StrQuote(const S: AnsiString; C: AnsiChar): AnsiString;
+var
+  L: SizeInt;
+begin
+  L := Length(S);
+  Result := S;
+  if L > 0 then
+  begin
+    if Result[1] <> C then
+    begin
+      Result := C + Result;
+      Inc(L);
+    end;
+    if Result[L] <> C then
+      Result := Result + C;
+  end;
+end;
+
+function StrRemoveChars(const S: AnsiString; const Chars: TSysCharSet): AnsiString;
+var
+  Source, Dest: PAnsiChar;
+  Index, Len: SizeInt;
+begin
+  Len := Length(S);
+  SetLength(Result, Len);
+  UniqueString(Result);
+  Source := PAnsiChar(S);
+  Dest := PAnsiChar(Result);
+  for Index := 0 to Len - 1 do
+  begin
+    if not (Source^ in Chars) then
+    begin
+      Dest^ := Source^;
+      Inc(Dest);
+    end;
+    Inc(Source);
+  end;
+  SetLength(Result, Dest - PAnsiChar(Result));
+end;
+
+function StrKeepChars(const S: AnsiString; const Chars: TSysCharSet): AnsiString;
+var
+  Source, Dest: PAnsiChar;
+  Index, Len: SizeInt;
+begin
+  Len := Length(S);
+  SetLength(Result, Len);
+  UniqueString(Result);
+  Source := PAnsiChar(S);
+  Dest := PAnsiChar(Result);
+  for Index := 0 to Len - 1 do
+  begin
+    if Source^ in Chars then
+    begin
+      Dest^ := Source^;
+      Inc(Dest);
+    end;
+    Inc(Source);
+  end;
+  SetLength(Result, Dest - PAnsiChar(Result));
+end;
+
+function StrRepeat(const S: AnsiString; Count: SizeInt): AnsiString;
+var
+  L: SizeInt;
+  P: PAnsiChar;
+begin
+  L := Length(S);
+  SetLength(Result, Count * L);
+  P := Pointer(Result);
+  if P <> nil then
+  begin
+    while Count > 0 do
+    begin
+      Move(Pointer(S)^, P^, L);
+      P := P + L;
+      Dec(Count);
+    end;
+  end;
+end;
+
+function StrRepeatLength(const S: AnsiString; const L: SizeInt): AnsiString;
+var
+  Count: SizeInt;
+  LenS: SizeInt;
+  P: PAnsiChar;
+begin
+  Result := '';
+  LenS := Length(S);
+
+  if (LenS > 0) and (S <> '') then
+  begin
+    Count := L div LenS;
+    if Count * LenS < L then
+      Inc(Count);
+    SetLength(Result, Count * LenS);
+    P := Pointer(Result);
+    while Count > 0 do
+    begin
+      Move(Pointer(S)^, P^, LenS);
+      P := P + LenS;
+      Dec(Count);
+    end;
+    if Length(S) > L then
+      SetLength(Result, L);
+  end;
+end;
+
+procedure StrReplace(var S: AnsiString; const Search, Replace: AnsiString; Flags: TReplaceFlags);
+var
+  SearchStr: AnsiString;
+  ResultStr: AnsiString;     { result string }
+  SourcePtr: PAnsiChar;      { pointer into S of character under examination }
+  SourceMatchPtr: PAnsiChar; { pointers into S and Search when first character has }
+  SearchMatchPtr: PAnsiChar; { been matched and we're probing for a complete match }
+  ResultPtr: PAnsiChar;      { pointer into Result of character being written }
+  ResultIndex: SizeInt;
+  SearchLength: SizeInt;     { length of search string }
+  ReplaceLength: SizeInt;    { length of replace string }
+  BufferLength: SizeInt;     { length of temporary result buffer }
+  ResultLength: SizeInt;     { length of result string }
+  C: AnsiChar;               { first character of search string }
+  IgnoreCase: Boolean;
+begin
+  if Search = '' then
+  begin
+    if S = '' then
+    begin
+      S := Replace;
+      Exit;
+    end
+    else
+      raise EJclAnsiStringError.CreateRes(@RsBlankSearchString);
+  end;
+
+  if S <> '' then
+  begin
+    IgnoreCase := rfIgnoreCase in Flags;
+    if IgnoreCase then
+      SearchStr := StrUpper(Search)
+    else
+      SearchStr := Search;
+    { avoid having to call Length() within the loop }
+    SearchLength := Length(Search);
+    ReplaceLength := Length(Replace);
+    ResultLength := Length(S);
+    BufferLength := ResultLength;
+    SetLength(ResultStr, BufferLength);
+    { get pointers to begin of source and result }
+    ResultPtr := PAnsiChar(ResultStr);
+    SourcePtr := PAnsiChar(S);
+    C := SearchStr[1];
+    { while we haven't reached the end of the string }
+    while True do
+    begin
+      { copy characters until we find the first character of the search string }
+      if IgnoreCase then
+        while (CharUpper(SourcePtr^) <> C) and (SourcePtr^ <> #0) do
+        begin
+          ResultPtr^ := SourcePtr^;
+          Inc(ResultPtr);
+          Inc(SourcePtr);
+        end
+      else
+        while (SourcePtr^ <> C) and (SourcePtr^ <> #0) do
+        begin
+          ResultPtr^ := SourcePtr^;
+          Inc(ResultPtr);
+          Inc(SourcePtr);
+        end;
+      { did we find that first character or did we hit the end of the string? }
+      if SourcePtr^ = #0 then
+        Break
+      else
+      begin
+        { continue comparing, +1 because first character was matched already }
+        SourceMatchPtr := SourcePtr + 1;
+        SearchMatchPtr := PAnsiChar(SearchStr) + 1;
+        if IgnoreCase then
+          while (CharUpper(SourceMatchPtr^) = SearchMatchPtr^) and (SearchMatchPtr^ <> #0) do
+          begin
+            Inc(SourceMatchPtr);
+            Inc(SearchMatchPtr);
+          end
+        else
+          while (SourceMatchPtr^ = SearchMatchPtr^) and (SearchMatchPtr^ <> #0) do
+          begin
+            Inc(SourceMatchPtr);
+            Inc(SearchMatchPtr);
+          end;
+        { did we find a complete match? }
+        if SearchMatchPtr^ = #0 then
+        begin
+          // keep track of result length
+          Inc(ResultLength, ReplaceLength - SearchLength);
+          if ReplaceLength > 0 then
+          begin
+            // increase buffer size if required
+            if ResultLength > BufferLength then
+            begin
+              BufferLength := ResultLength * 2;
+              ResultIndex := ResultPtr - PAnsiChar(ResultStr) + 1;
+              SetLength(ResultStr, BufferLength);
+              ResultPtr := @ResultStr[ResultIndex];
+            end;
+            { append replace to result and move past the search string in source }
+            Move((@Replace[1])^, ResultPtr^, ReplaceLength);
+          end;
+          Inc(SourcePtr, SearchLength);
+          Inc(ResultPtr, ReplaceLength);
+          { replace all instances or just one? }
+          if not (rfReplaceAll in Flags) then
+          begin
+            { just one, copy until end of source and break out of loop }
+            while SourcePtr^ <> #0 do
+            begin
+              ResultPtr^ := SourcePtr^;
+              Inc(ResultPtr);
+              Inc(SourcePtr);
+            end;
+            Break;
+          end;
+        end
+        else
+        begin
+          { copy current character and start over with the next }
+          ResultPtr^ := SourcePtr^;
+          Inc(ResultPtr);
+          Inc(SourcePtr);
+        end;
+      end;
+    end;
+    { set result length and copy result into S }
+    SetLength(ResultStr, ResultLength);
+    S := ResultStr;
+  end;
+end;
+
+function StrReplaceChar(const S: AnsiString; const Source, Replace: AnsiChar): AnsiString;
+var
+  I: SizeInt;
+begin
+  Result := S;
+  for I := 1 to Length(S) do
+    if Result[I] = Source then
+      Result[I] := Replace;
+end;
+
+function StrReplaceChars(const S: AnsiString; const Chars: TSysCharSet; Replace: AnsiChar): AnsiString;
+var
+  I: SizeInt;
+begin
+  Result := S;
+  for I := 1 to Length(S) do
+    if Result[I] in Chars then
+      Result[I] := Replace;
+end;
+
+function StrReplaceButChars(const S: AnsiString; const Chars: TSysCharSet;
+  Replace: AnsiChar): AnsiString;
+var
+  I: SizeInt;
+begin
+  Result := S;
+  for I := 1 to Length(S) do
+    if not (Result[I] in Chars) then
+      Result[I] := Replace;
+end;
+
+function StrReverse(const S: AnsiString): AnsiString;
+begin
+  Result := S;
+  StrReverseInplace(Result);
+end;
+
+procedure StrReverseInPlace(var S: AnsiString);
+var
+  P1, P2: PAnsiChar;
+  C: AnsiChar;
+begin
+  UniqueString(S);
+  P1 := PAnsiChar(S);
+  P2 := P1 + SizeOf(AnsiChar) * (Length(S) - 1);
+  while P1 < P2 do
+  begin
+    C := P1^;
+    P1^ := P2^;
+    P2^ := C;
+    Inc(P1);
+    Dec(P2);
+  end;
+end;
+
+function StrSingleQuote(const S: AnsiString): AnsiString;
+begin
+  Result := AnsiSingleQuote + S + AnsiSingleQuote;
+end;
+
+procedure StrSkipChars(var S: PAnsiChar; const Chars: TSysCharSet);
+begin
+  while S^ in Chars do
+    Inc(S);
+end;
+
+procedure StrSkipChars(const S: AnsiString; var Index: SizeInt; const Chars: TSysCharSet);
+begin
+  while S[Index] in Chars do
+    Inc(Index);
+end;
+
+function StrSmartCase(const S: AnsiString; Delimiters: TSysCharSet): AnsiString;
+var
+  Source, Dest: PAnsiChar;
+  Index, Len: SizeInt;
+begin
+  Result := '';
+  if Delimiters = [] then
+    Include(Delimiters, AnsiSpace);
+
+  if S <> '' then
+  begin
+    Result := S;
+    UniqueString(Result);
+
+    Len := Length(S);
+    Source := PAnsiChar(S);
+    Dest := PAnsiChar(Result);
+    Inc(Dest);
+
+    for Index := 2 to Len do
+    begin
+      if (Source^ in Delimiters) then
+        Dest^ := CharUpper(Dest^);
+
+      Inc(Dest);
+      Inc(Source);
+    end;
+
+    Result[1] := CharUpper(Result[1]);
+  end;
+end;
+
+function StrStringToEscaped(const S: AnsiString): AnsiString;
+var
+  I: SizeInt;
+begin
+  Result := '';
+  for I := 1 to Length(S) do
+  begin
+    case S[I] of
+      AnsiBackspace:
+        Result := Result + '\b';
+      AnsiBell:
+        Result := Result + '\a';
+      AnsiCarriageReturn:
+        Result := Result + '\r';
+      AnsiFormFeed:
+        Result := Result + '\f';
+      AnsiLineFeed:
+        Result := Result + '\n';
+      AnsiTab:
+        Result := Result + '\t';
+      AnsiVerticalTab:
+        Result := Result + '\v';
+      '\':
+        Result := Result + '\\';
+      '"':
+        Result := Result + '\"';
+    else
+      // Characters < ' ' are escaped with hex sequence
+      if S[I] < #32 then
+        Result := Result + AnsiString(Format('\x%.2x', [SizeInt(S[I])]))
+      else
+        Result := Result + S[I];
+    end;
+  end;
+end;
+
+function StrStripNonNumberChars(const S: AnsiString): AnsiString;
+var
+  I: SizeInt;
+  C: AnsiChar;
+begin
+  Result := '';
+  for I := 1 to Length(S) do
+  begin
+    C := S[I];
+    if CharIsNumberChar(C) then
+      Result := Result + C;
+  end;
+end;
+
+function StrToHex(const Source: AnsiString): AnsiString;
+var
+  Index: SizeInt;
+  C, L, N: SizeInt;
+  BL, BH: Byte;
+  S: AnsiString;
+begin
+  Result := '';
+  if Source <> '' then
+  begin
+    S := Source;
+    L := Length(S);
+    if Odd(L) then
+    begin
+      S := '0' + S;
+      Inc(L);
+    end;
+    Index := 1;
+    SetLength(Result, L div 2);
+    C := 1;
+    N := 1;
+    while C <= L do
+    begin
+      BH := CharHex(S[Index]);
+      Inc(Index);
+      BL := CharHex(S[Index]);
+      Inc(Index);
+      Inc(C, 2);
+      if (BH = $FF) or (BL = $FF) then
+      begin
+        Result := '';
+        Exit;
+      end;
+      Result[N] := AnsiChar((Cardinal(BH) shl 4) or Cardinal(BL));
+      Inc(N);
+    end;
+  end;
+end;
+
+function StrTrimCharLeft(const S: AnsiString; C: AnsiChar): AnsiString;
+var
+  I, L: SizeInt;
+begin
+  I := 1;
+  L := Length(S);
+  while (I <= L) and (S[I] = C) do
+    Inc(I);
+  Result := Copy(S, I, L - I + 1);
+end;
+
+function StrTrimCharsLeft(const S: AnsiString; const Chars: TSysCharSet): AnsiString;
+var
+  I, L: SizeInt;
+begin
+  I := 1;
+  L := Length(S);
+  while (I <= L) and (S[I] in Chars) do
+    Inc(I);
+  Result := Copy(S, I, L - I + 1);
+end;
+
+function StrTrimCharsRight(const S: AnsiString; const Chars: TSysCharSet): AnsiString;
+var
+  I: SizeInt;
+begin
+  I := Length(S);
+  while (I >= 1) and (S[I] in Chars) do
+    Dec(I);
+  Result := Copy(S, 1, I);
+end;
+
+function StrTrimCharRight(const S: AnsiString; C: AnsiChar): AnsiString;
+var
+  I: SizeInt;
+begin
+  I := Length(S);
+  while (I >= 1) and (S[I] = C) do
+    Dec(I);
+  Result := Copy(S, 1, I);
+end;
+
+function StrTrimQuotes(const S: AnsiString): AnsiString;
+var
+  First, Last: AnsiChar;
+  L: SizeInt;
+begin
+  L := Length(S);
+  if L > 1 then
+  begin
+    First := S[1];
+    Last := S[L];
+    if (First = Last) and ((First = AnsiSingleQuote) or (First = AnsiDoubleQuote)) then
+      Result := Copy(S, 2, L - 2)
+    else
+      Result := S;
+  end
+  else
+    Result := S;
+end;
+
+function StrTrimQuotes(const S: AnsiString; QuoteChar: AnsiChar): AnsiString;
+var
+  First, Last: AnsiChar;
+  L: SizeInt;
+begin
+  L := Length(S);
+  if L > 1 then
+  begin
+    First := S[1];
+    Last := S[L];
+    if (First = Last) and (First = QuoteChar) then
+      Result := Copy(S, 2, L - 2)
+    else
+      Result := S;
+  end
+  else
+    Result := S;
+end;
+
+function StrUpper(const S: AnsiString): AnsiString;
+begin
+  Result := S;
+  StrUpperInPlace(Result);
+end;
+
+procedure StrUpperInPlace(var S: AnsiString);
+begin
+  StrCase(S, AnsiUpOffset);
+end;
+
+procedure StrUpperBuff(S: PAnsiChar);
+begin
+  StrCaseBuff(S, AnsiUpOffset);
+end;
+
+{$IFDEF MSWINDOWS}
+function StrOemToAnsi(const S: AnsiString): AnsiString;
+begin
+  SetLength(Result, Length(S));
+  OemToAnsiBuff(PAnsiChar(S), PAnsiChar(Result), Length(S));
+end;
+
+function StrAnsiToOem(const S: AnsiString): AnsiString;
+begin
+  SetLength(Result, Length(S));
+  AnsiToOemBuff(PAnsiChar(S), PAnsiChar(Result), Length(S));
+end;
+{$ENDIF MSWINDOWS}
+
+//=== String Management ======================================================
+
+procedure StrAddRef(var S: AnsiString);
+var
+  P: PAnsiStrRec;
+begin
+  P := Pointer(S);
+  if P <> nil then
+  begin
+    Dec(P);
+    if P^.RefCount = -1 then
+      UniqueString(S)
+    else
+      LockedInc(P^.RefCount);
+  end;
+end;
+
+procedure StrDecRef(var S: AnsiString);
+var
+  P: PAnsiStrRec;
+begin
+  P := Pointer(S);
+  if P <> nil then
+  begin
+    Dec(P);
+    case P^.RefCount of
+      -1, 0:
+        { nothing } ;
+      1:
+        begin
+          Finalize(S);
+          Pointer(S) := nil;
+        end;
+    else
+      LockedDec(P^.RefCount);
+    end;
+  end;
+end;
+
+function StrLength(const S: AnsiString): Longint;
+var
+  P: PAnsiStrRec;
+begin
+  Result := 0;
+  P := Pointer(S);
+  if P <> nil then
+  begin
+    Dec(P);
+    Result := P^.Length and (not $80000000 shr 1);
+  end;
+end;
+
+function StrRefCount(const S: AnsiString): Longint;
+var
+  P: PAnsiStrRec;
+begin
+  Result := 0;
+  P := Pointer(S);
+  if P <> nil then
+  begin
+    Dec(P);
+    Result := P^.RefCount;
+  end;
+end;
+
+procedure StrResetLength(var S: AnsiString);
+var
+  I: SizeInt;
+begin
+  for I := 0 to Length(S) - 1 do
+    if S[I + 1] = #0 then
+    begin
+      SetLength(S, I);
+      Exit;
+    end;
+end;
+
+//=== String Search and Replace Routines =====================================
+
+function StrCharCount(const S: AnsiString; C: AnsiChar): SizeInt;
+var
+  I: SizeInt;
+begin
+  Result := 0;
+  for I := 1 to Length(S) do
+    if S[I] = C then
+      Inc(Result);
+end;
+
+function StrCharsCount(const S: AnsiString; Chars: TSysCharSet): SizeInt;
+var
+  I: SizeInt;
+begin
+  Result := 0;
+  for I := 1 to Length(S) do
+    if S[I] in Chars then
+      Inc(Result);
+end;
+
+function StrStrCount(const S, SubS: AnsiString): SizeInt;
+var
+  I: SizeInt;
+begin
+  Result := 0;
+  if (Length(SubS) > Length(S)) or (Length(SubS) = 0) or (Length(S) = 0) then
+    Exit;
+  if Length(SubS) = 1 then
+  begin
+    Result := StrCharCount(S, SubS[1]);
+    Exit;
+  end;
+  I := StrSearch(SubS, S, 1);
+
+  if I > 0 then
+    Inc(Result);
+
+  while (I > 0) and (Length(S) > I + Length(SubS)) do
+  begin
+    I := StrSearch(SubS, S, I + 1);
+    if I > 0 then
+      Inc(Result);
+  end;
+end;
+
+(*
+{ 1}  Test(StrCompareRange('', '', 1, 5), 0);
+{ 2}  Test(StrCompareRange('A', '', 1, 5), -1);
+{ 3}  Test(StrCompareRange('AB', '', 1, 5), -1);
+{ 4}  Test(StrCompareRange('ABC', '', 1, 5), -1);
+{ 5}  Test(StrCompareRange('', 'A', 1, 5), -1);
+{ 6}  Test(StrCompareRange('', 'AB',  1, 5), -1);
+{ 7}  Test(StrCompareRange('', 'ABC', 1, 5), -1);
+{ 8}  Test(StrCompareRange('A', 'a', 1, 5), -2);
+{ 9}  Test(StrCompareRange('A', 'a', 1, 1), -32);
+{10}  Test(StrCompareRange('aA', 'aB', 1, 1), 0);
+{11}  Test(StrCompareRange('aA', 'aB', 1, 2), -1);
+{12}  Test(StrCompareRange('aB', 'aA', 1, 2), 1);
+{13}  Test(StrCompareRange('aA', 'aa', 1, 2), -32);
+{14}  Test(StrCompareRange('aa', 'aA', 1, 2), 32);
+{15}  Test(StrCompareRange('', '', 1, 0), 0);
+{16}  Test(StrCompareRange('A', 'A', 1, 0), -2);
+{17}  Test(StrCompareRange('Aa', 'A', 1, 0), -2);
+{18}  Test(StrCompareRange('Aa', 'Aa', 1, 2), 0);
+{19}  Test(StrCompareRange('Aa', 'A', 1, 2), 0);
+{20}  Test(StrCompareRange('Ba', 'A', 1, 2), 1);
+*)
+function StrCompareRangeEx(const S1, S2: AnsiString; Index, Count: SizeInt; CaseSensitive: Boolean): SizeInt;
+var
+  Len1, Len2: SizeInt;
+  I: SizeInt;
+  C1, C2: AnsiChar;
+begin
+  if Pointer(S1) = Pointer(S2) then
+  begin
+    if (Count <= 0) and (S1 <> '') then
+      Result := -2 // no work
+    else
+      Result := 0;
+  end
+  else
+  if (S1 = '') or (S2 = '') then
+    Result := -1 // null string
+  else
+  if Count <= 0 then
+    Result := -2 // no work
+  else
+  begin
+    Len1 := Length(S1);
+    Len2 := Length(S2);
+
+    if (Index - 1) + Count > Len1 then
+      Result := -2
+    else
+    begin
+      if (Index - 1) + Count > Len2 then // strange behaviour, but the assembler code does it
+        Count := Len2 - (Index - 1);
+
+      if CaseSensitive then
+      begin
+        for I := 0 to Count - 1 do
+        begin
+          C1 := S1[Index + I];
+          C2 := S2[Index + I];
+          if C1 <> C2 then
+          begin
+            Result := Ord(C1) - Ord(C2);
+            Exit;
+          end;
+        end;
+      end
+      else
+      begin
+        for I := 0 to Count - 1 do
+        begin
+          C1 := S1[Index + I];
+          C2 := S2[Index + I];
+          if C1 <> C2 then
+          begin
+            C1 := CharLower(C1);
+            C2 := CharLower(C2);
+            if C1 <> C2 then
+            begin
+              Result := Ord(C1) - Ord(C2);
+              Exit;
+            end;
+          end;
+        end;
+      end;
+      Result := 0;
+    end;
+  end;
+end;
+
+function StrCompare(const S1, S2: AnsiString; CaseSensitive: Boolean): SizeInt;
+var
+  Len1, Len2: SizeInt;
+begin
+  if Pointer(S1) = Pointer(S2) then
+    Result := 0
+  else
+  begin
+    Len1 := Length(S1);
+    Len2 := Length(S2);
+    Result := Len1 - Len2;
+    if Result = 0 then
+      Result := StrCompareRangeEx(S1, S2, 1, Len1, CaseSensitive);
+  end;
+end;
+
+function StrCompareRange(const S1, S2: AnsiString; Index, Count: SizeInt; CaseSensitive: Boolean): SizeInt;
+begin
+  Result := StrCompareRangeEx(S1, S2, Index, Count, CaseSensitive);
+end;
+
+function StrRepeatChar(C: AnsiChar; Count: SizeInt): AnsiString;
+begin
+  SetLength(Result, Count);
+  if Count > 0 then
+    FillChar(Result[1], Count, C);
+end;
+
+function StrFind(const Substr, S: AnsiString; const Index: SizeInt): SizeInt;
+var
+  pos: SizeInt;
+begin
+  if (SubStr <> '') and (S <> '') then
+  begin
+    pos := StrIPos(Substr, Copy(S, Index, Length(S) - Index + 1));
+    if pos = 0 then
+      Result := 0
+    else
+      Result := Index + Pos - 1;
+  end
+  else
+    Result := 0;
+end;
+
+function StrHasPrefix(const S: AnsiString; const Prefixes: array of AnsiString): Boolean;
+begin
+  Result := StrPrefixIndex(S, Prefixes) > -1;
+end;
+
+function StrHasSuffix(const S: AnsiString; const Suffixes: array of AnsiString): Boolean;
+begin
+  Result := StrSuffixIndex(S, Suffixes) > -1;
+end;
+
+function StrIHasPrefix(const S: AnsiString; const Prefixes: array of AnsiString): Boolean;
+begin
+  Result := StrIPrefixIndex(S, Prefixes) > -1;
+end;
+
+function StrIHasSuffix(const S: AnsiString; const Suffixes: array of AnsiString): Boolean;
+begin
+  Result := StrISuffixIndex(S, Suffixes) > -1;
+end;
+
+function StrIndex(const S: AnsiString; const List: array of AnsiString; CaseSensitive: Boolean): SizeInt;
+var
+  I: SizeInt;
+begin
+  Result := -1;
+  for I := Low(List) to High(List) do
+  begin
+    if StrCompare(S, List[I], CaseSensitive) = 0 then
+    begin
+      Result := I;
+      Break;
+    end;
+  end;
+end;
+
+function StrILastPos(const SubStr, S: AnsiString): SizeInt;
+begin
+  Result := StrLastPos(StrUpper(SubStr), StrUpper(S));
+end;
+
+function StrIPos(const SubStr, S: AnsiString): SizeInt;
+begin
+  Result := Pos(StrUpper(SubStr), StrUpper(S));
+end;
+
+function StrIPrefixIndex(const S: AnsiString; const Prefixes: array of AnsiString): SizeInt;
+var
+  I: SizeInt;
+  Test: AnsiString;
+begin
+  Result := -1;
+  for I := Low(Prefixes) to High(Prefixes) do
+  begin
+    Test := StrLeft(S, Length(Prefixes[I]));
+    if CompareText(Test, Prefixes[I]) = 0 then
+    begin
+      Result := I;
+      Break;
+    end;
+  end;
+end;
+
+function StrIsOneOf(const S: AnsiString; const List: array of AnsiString): Boolean;
+begin
+  Result := StrIndex(S, List) > -1;
+end;
+
+function StrISuffixIndex(const S: AnsiString; const Suffixes: array of AnsiString): SizeInt;
+var
+  I: SizeInt;
+  Test: AnsiString;
+begin
+  Result := -1;
+  for I := Low(Suffixes) to High(Suffixes) do
+  begin
+    Test := StrRight(S, Length(Suffixes[I]));
+    if CompareText(Test, Suffixes[I]) = 0 then
+    begin
+      Result := I;
+      Break;
+    end;
+  end;
+end;
+
+function StrLastPos(const SubStr, S: AnsiString): SizeInt;
+var
+  Last, Current: PAnsiChar;
+begin
+  Result := 0;
+  Last := nil;
+  Current := PAnsiChar(S);
+
+  while (Current <> nil) and (Current^ <> #0) do
+  begin
+    Current := AnsiStrPosA(PAnsiChar(Current), PAnsiChar(SubStr));
+    if Current <> nil then
+    begin
+      Last := Current;
+      Inc(Current);
+    end;
+  end;
+  if Last <> nil then
+    Result := Abs(PAnsiChar(S) - Last) + 1;
+end;
+
+// IMPORTANT NOTE: The StrMatch function does currently not work with the Asterix (*)
+// (*) acts like (?)
+
+function StrMatch(const Substr, S: AnsiString; Index: SizeInt): SizeInt;
+var
+  SI, SubI, SLen, SubLen: SizeInt;
+  SubC: AnsiChar;
+begin
+  SLen := Length(S);
+  SubLen := Length(Substr);
+  Result := 0;
+  if (Index > SLen) or (SubLen = 0) then
+    Exit;
+  while Index <= SLen do
+  begin
+    SubI := 1;
+    SI := Index;
+    while (SI <= SLen) and (SubI <= SubLen) do
+    begin
+      SubC := Substr[SubI];
+      if (SubC = '*') or (SubC = '?') or (SubC = S[SI]) then
+      begin
+        Inc(SI);
+        Inc(SubI);
+      end
+      else
+        Break;
+    end;
+    if SubI > SubLen then
+    begin
+      Result := Index;
+      Break;
+    end;
+    Inc(Index);
+  end;
+end;
+
+
+// Derived from "Like" by Michael Winter
+
+function StrMatches(const Substr, S: AnsiString; const Index: SizeInt): Boolean;
+var
+  StringPtr: PAnsiChar;
+  PatternPtr: PAnsiChar;
+  StringRes: PAnsiChar;
+  PatternRes: PAnsiChar;
+begin
+  if SubStr = '' then
+    raise EJclAnsiStringError.CreateRes(@RsBlankSearchString);
+
+  Result := SubStr = '*';
+
+  if Result or (S = '') then
+    Exit;
+
+  if (Index <= 0) or (Index > Length(S)) then
+    raise EJclAnsiStringError.CreateRes(@RsArgumentOutOfRange);
+
+  StringPtr := PAnsiChar(@S[Index]);
+  PatternPtr := PAnsiChar(SubStr);
+  StringRes := nil;
+  PatternRes := nil;
+
+  repeat
+    repeat
+      case PatternPtr^ of
+        #0:
+          begin
+            Result := StringPtr^ = #0;
+            if Result or (StringRes = nil) or (PatternRes = nil) then
+              Exit;
+
+            StringPtr := StringRes;
+            PatternPtr := PatternRes;
+            Break;
+          end;
+        '*':
+          begin
+            Inc(PatternPtr);
+            PatternRes := PatternPtr;
+            Break;
+          end;
+        '?':
+          begin
+            if StringPtr^ = #0 then
+              Exit;
+            Inc(StringPtr);
+            Inc(PatternPtr);
+          end;
+        else
+          begin
+            if StringPtr^ = #0 then
+              Exit;
+            if StringPtr^ <> PatternPtr^ then
+            begin
+              if (StringRes = nil) or (PatternRes = nil) then
+                Exit;
+              StringPtr := StringRes;
+              PatternPtr := PatternRes;
+              Break;
+            end
+            else
+            begin
+              Inc(StringPtr);
+              Inc(PatternPtr);
+            end;
+          end;
+      end;
+    until False;
+
+    repeat
+      case PatternPtr^ of
+        #0:
+          begin
+            Result := True;
+            Exit;
+          end;
+        '*':
+          begin
+            Inc(PatternPtr);
+            PatternRes := PatternPtr;
+          end;
+        '?':
+          begin
+            if StringPtr^ = #0 then
+              Exit;
+            Inc(StringPtr);
+            Inc(PatternPtr);
+          end;
+        else
+          begin
+            repeat
+              if StringPtr^ = #0 then
+                Exit;
+              if StringPtr^ = PatternPtr^ then
+                Break;
+              Inc(StringPtr);
+            until False;
+            Inc(StringPtr);
+            StringRes := StringPtr;
+            Inc(PatternPtr);
+            Break;
+          end;
+      end;
+    until False;
+  until False;
+end;
+
+function StrNPos(const S, SubStr: AnsiString; N: SizeInt): SizeInt;
+var
+  I, P: SizeInt;
+begin
+  if N < 1 then
+  begin
+    Result := 0;
+    Exit;
+  end;
+
+  Result := StrSearch(SubStr, S, 1);
+  I := 1;
+  while I < N do
+  begin
+    P := StrSearch(SubStr, S, Result + 1);
+    if P = 0 then
+    begin
+      Result := 0;
+      Break;
+    end
+    else
+    begin
+      Result := P;
+      Inc(I);
+    end;
+  end;
+end;
+
+function StrNIPos(const S, SubStr: AnsiString; N: SizeInt): SizeInt;
+var
+  I, P: SizeInt;
+begin
+  if N < 1 then
+  begin
+    Result := 0;
+    Exit;
+  end;
+
+  Result := StrFind(SubStr, S, 1);
+  I := 1;
+  while I < N do
+  begin
+    P := StrFind(SubStr, S, Result + 1);
+    if P = 0 then
+    begin
+      Result := 0;
+      Break;
+    end
+    else
+    begin
+      Result := P;
+      Inc(I);
+    end;
+  end;
+end;
+
+function StrPrefixIndex(const S: AnsiString; const Prefixes: array of AnsiString): SizeInt;
+var
+  I: SizeInt;
+  Test: AnsiString;
+begin
+  Result := -1;
+  for I := Low(Prefixes) to High(Prefixes) do
+  begin
+    Test := StrLeft(S, Length(Prefixes[I]));
+    if CompareStr(Test, Prefixes[I]) = 0 then
+    begin
+      Result := I;
+      Break;
+    end;
+  end;
+end;
+
+function StrSearch(const Substr, S: AnsiString; const Index: SizeInt): SizeInt;
+var
+  SP, SPI, SubP: PAnsiChar;
+  SLen: SizeInt;
+begin
+  SLen := Length(S);
+  if Index <= SLen then
+  begin
+    SP := PAnsiChar(S);
+    SubP := PAnsiChar(Substr);
+    SPI := SP;
+    Inc(SPI, Index);
+    Dec(SPI);
+    SPI := StrPosA(SPI, SubP);
+    if SPI <> nil then
+      Result := SPI - SP + 1
+    else
+      Result := 0;
+  end
+  else
+    Result := 0;
+end;
+
+function StrSuffixIndex(const S: AnsiString; const Suffixes: array of AnsiString): SizeInt;
+var
+  I: SizeInt;
+  Test: AnsiString;
+begin
+  Result := -1;
+  for I := Low(Suffixes) to High(Suffixes) do
+  begin
+    Test := StrRight(S, Length(Suffixes[I]));
+    if CompareStr(Test, Suffixes[I]) = 0 then
+    begin
+      Result := I;
+      Break;
+    end;
+  end;
+end;
+
+//=== String Extraction ======================================================
+
+function StrAfter(const SubStr, S: AnsiString): AnsiString;
+var
+  P: SizeInt;
+begin
+  P := StrFind(SubStr, S, 1); // StrFind is case-insensitive pos
+  if P <= 0 then
+    Result := ''           // substr not found -> nothing after it
+  else
+    Result := StrRestOf(S, P + Length(SubStr));
+end;
+
+function StrBefore(const SubStr, S: AnsiString): AnsiString;
+var
+  P: SizeInt;
+begin
+  P := StrFind(SubStr, S, 1);
+  if P <= 0 then
+    Result := S
+  else
+    Result := StrLeft(S, P - 1);
+end;
+
+function StrSplit(const SubStr, S: AnsiString;var Left, Right : AnsiString): boolean;
+var
+  P: SizeInt;
+begin
+  P := StrFind(SubStr, S, 1);
+  Result:= p > 0;
+  if Result then
+  begin
+    Left := StrLeft(S, P - 1);
+    Right := StrRestOf(S, P + Length(SubStr));
+  end
+  else
+  begin
+    Left := '';
+    Right := '';
+  end;
+end;
+
+function StrBetween(const S: AnsiString; const Start, Stop: AnsiChar): AnsiString;
+var
+  PosStart, PosEnd: SizeInt;
+  L: SizeInt;
+begin
+  PosStart := Pos(Start, S);
+  PosEnd := StrSearch(Stop, S, PosStart + 1);  // PosEnd has to be after PosStart.
+
+  if (PosStart > 0) and (PosEnd > PosStart) then
+  begin
+    L := PosEnd - PosStart;
+    Result := Copy(S, PosStart + 1, L - 1);
+  end
+  else
+    Result := '';
+end;
+
+function StrChopRight(const S: AnsiString; N: SizeInt): AnsiString;
+begin
+  Result := Copy(S, 1, Length(S) - N);
+end;
+
+function StrLeft(const S: AnsiString; Count: SizeInt): AnsiString;
+begin
+  Result := Copy(S, 1, Count);
+end;
+
+function StrMid(const S: AnsiString; Start, Count: SizeInt): AnsiString;
+begin
+  Result := Copy(S, Start, Count);
+end;
+
+function StrRestOf(const S: AnsiString; N: SizeInt): AnsiString;
+begin
+  Result := Copy(S, N, (Length(S) - N + 1));
+end;
+
+function StrRight(const S: AnsiString; Count: SizeInt): AnsiString;
+begin
+  Result := Copy(S, Length(S) - Count + 1, Count);
+end;
+
+//=== Character (do we have it ;) ============================================
+
+function CharEqualNoCase(const C1, C2: AnsiChar): Boolean;
+begin
+  // if they are not equal chars, may be same letter different case
+  Result := (C1 = C2) or
+    (CharIsAlpha(C1) and CharIsAlpha(C2) and (CharLower(C1) = CharLower(C2)));
+end;
+
+
+function CharIsAlpha(const C: AnsiChar): Boolean;
+begin
+  Result := (AnsiCharTypes[C] and C1_ALPHA) <> 0;
+end;
+
+function CharIsAlphaNum(const C: AnsiChar): Boolean;
+begin
+  Result := ((AnsiCharTypes[C] and C1_ALPHA) <> 0) or
+    ((AnsiCharTypes[C] and C1_DIGIT) <> 0);
+end;
+
+function CharIsBlank(const C: AnsiChar): Boolean;
+begin
+  Result := ((AnsiCharTypes[C] and C1_BLANK) <> 0);
+end;
+
+function CharIsControl(const C: AnsiChar): Boolean;
+begin
+  Result := (AnsiCharTypes[C] and C1_CNTRL) <> 0;
+end;
+
+function CharIsDelete(const C: AnsiChar): Boolean;
+begin
+  Result := (C = #8);
+end;
+
+function CharIsDigit(const C: AnsiChar): Boolean;
+begin
+  Result := (AnsiCharTypes[C] and C1_DIGIT) <> 0;
+end;
+
+function CharIsFracDigit(const C: AnsiChar): Boolean;
+begin
+  Result := (C = '.') or ((AnsiCharTypes[C] and C1_DIGIT) <> 0);
+end;
+
+function CharIsHexDigit(const C: AnsiChar): Boolean;
+begin
+  case C of
+    'A'..'F',
+    'a'..'f':
+      Result := True;
+  else
+    Result := ((AnsiCharTypes[C] and C1_DIGIT) <> 0);
+  end;
+end;
+
+function CharIsLower(const C: AnsiChar): Boolean;
+begin
+  Result := (AnsiCharTypes[C] and C1_LOWER) <> 0;
+end;
+
+// JclSysUtils.TJclFormatSettings.GetDecimalSeparator is manually inlined in the 2 following functions
+// this fixes compiler warnings about functions not being inlined
+ 
+function CharIsNumberChar(const C: AnsiChar): Boolean;
+begin
+  Result := ((AnsiCharTypes[C] and C1_DIGIT) <> 0) or (C = AnsiSignMinus) or (C = AnsiSignPlus) or
+    (Char(C) = {$IFDEF RTL220_UP}FormatSettings.DecimalSeparator{$ELSE}SysUtils.DecimalSeparator{$ENDIF});
+end;
+
+function CharIsNumber(const C: AnsiChar): Boolean;
+begin
+  Result := ((AnsiCharTypes[C] and C1_DIGIT) <> 0) or
+    (Char(C) = {$IFDEF RTL220_UP}FormatSettings.DecimalSeparator{$ELSE}SysUtils.DecimalSeparator{$ENDIF});
+end;
+
+function CharIsPrintable(const C: AnsiChar): Boolean;
+begin
+  Result := not CharIsControl(C);
+end;
+
+function CharIsPunctuation(const C: AnsiChar): Boolean;
+begin
+  Result := ((AnsiCharTypes[C] and C1_PUNCT) <> 0);
+end;
+
+function CharIsReturn(const C: AnsiChar): Boolean;
+begin
+  Result := (C = AnsiLineFeed) or (C = AnsiCarriageReturn);
+end;
+
+function CharIsSpace(const C: AnsiChar): Boolean;
+begin
+  Result := (AnsiCharTypes[C] and C1_SPACE) <> 0;
+end;
+
+function CharIsUpper(const C: AnsiChar): Boolean;
+begin
+  Result := (AnsiCharTypes[C] and C1_UPPER) <> 0;
+end;
+
+function CharIsValidIdentifierLetter(const C: AnsiChar): Boolean;
+begin
+  case C of
+    '0'..'9', 'A'..'Z', 'a'..'z', '_':
+      Result := True;
+  else
+    Result := False;
+  end;
+end;
+
+function CharIsWhiteSpace(const C: AnsiChar): Boolean;
+begin
+  Result := (C = AnsiTab) or (C = AnsiLineFeed) or (C = AnsiVerticalTab) or
+            (C = AnsiFormFeed) or (C = AnsiCarriageReturn) or (C =AnsiSpace) or
+            ((AnsiCharTypes[C] and C1_SPACE) <> 0);
+end;
+
+function CharIsWildcard(const C: AnsiChar): Boolean;
+begin
+  case C of
+    '*', '?':
+      Result := True;
+  else
+    Result := False;
+  end;
+end;
+
+function CharType(const C: AnsiChar): Word;
+begin
+  Result := AnsiCharTypes[C];
+end;
+
+//=== PCharVector ============================================================
+
+function StringsToPCharVector(var Dest: PAnsiCharVector; const Source: TJclAnsiStrings): PAnsiCharVector;
+var
+  I: SizeInt;
+  S: AnsiString;
+  List: array of PAnsiChar;
+begin
+  Assert(Source <> nil);
+  Dest := AllocMem((Source.Count + SizeOf(AnsiChar)) * SizeOf(PAnsiChar));
+  SetLength(List, Source.Count + SizeOf(AnsiChar));
+  for I := 0 to Source.Count - 1 do
+  begin
+    S := Source[I];
+    {$IFDEF SUPPORTS_UNICODE}
+    List[I] := AnsiStrAlloc(Length(S) + SizeOf(AnsiChar));
+    {$ELSE ~SUPPORTS_UNICODE}
+    List[I] := StrAlloc(Length(S) + SizeOf(AnsiChar));
+    {$ENDIF ~SUPPORTS_UNICODE}
+    StrPCopyA(List[I], S);
+  end;
+  List[Source.Count] := nil;
+  Move(List[0], Dest^, (Source.Count + 1) * SizeOf(PAnsiChar));
+  Result := Dest;
+end;
+
+function PCharVectorCount(Source: PAnsiCharVector): SizeInt;
+begin
+  Result := 0;
+  if Source <> nil then
+    while Source^ <> nil do
+  begin
+    Inc(Source);
+    Inc(Result);
+  end;
+end;
+
+procedure PCharVectorToStrings(const Dest: TJclAnsiStrings; Source: PAnsiCharVector);
+var
+  I, Count: SizeInt;
+  List: array of PAnsiChar;
+begin
+  Assert(Dest <> nil);
+  if Source <> nil then
+  begin
+    Count := PCharVectorCount(Source);
+    SetLength(List, Count);
+    Move(Source^, List[0], Count * SizeOf(PAnsiChar));
+    Dest.BeginUpdate;
+    try
+      Dest.Clear;
+      for I := 0 to Count - 1 do
+        Dest.Add(List[I]);
+    finally
+      Dest.EndUpdate;
+    end;
+  end;
+end;
+
+procedure FreePCharVector(var Dest: PAnsiCharVector);
+var
+  I, Count: SizeInt;
+  List: array of PAnsiChar;
+begin
+  if Dest <> nil then
+  begin
+    Count := PCharVectorCount(Dest);
+    SetLength(List, Count);
+    Move(Dest^, List[0], Count * SizeOf(PAnsiChar));
+    for I := 0 to Count - 1 do
+      StrDisposeA(List[I]);
+    FreeMem(Dest, (Count + 1) * SizeOf(PAnsiChar));
+    Dest := nil;
+  end;
+end;
+
+//=== Character Transformation Routines ======================================
+
+function CharHex(const C: AnsiChar): Byte;
+begin
+  case C of
+    '0'..'9':
+      Result := Ord(C) - Ord('0');
+    'a'..'f':
+      Result := Ord(C) - Ord('a') + 10;
+    'A'..'F':
+      Result := Ord(C) - Ord('A') + 10;
+  else
+    Result := $FF;
+  end;
+end;
+
+function CharLower(const C: AnsiChar): AnsiChar;
+begin
+  Result := AnsiCaseMap[Ord(C) + AnsiLoOffset];
+end;
+
+function CharToggleCase(const C: AnsiChar): AnsiChar;
+begin
+  Result := AnsiCaseMap[Ord(C) + AnsiReOffset];
+end;
+
+function CharUpper(const C: AnsiChar): AnsiChar;
+begin
+  Result := AnsiCaseMap[Ord(C) + AnsiUpOffset];
+end;
+
+//=== Character Search and Replace ===========================================
+
+function CharLastPos(const S: AnsiString; const C: AnsiChar; const Index: SizeInt): SizeInt;
+begin
+  if (Index > 0) and (Index <= Length(S)) then
+    for Result := Length(S) downto Index do
+      if S[Result] = C then
+        Exit;
+  Result := 0;
+end;
+
+function CharPos(const S: AnsiString; const C: AnsiChar; const Index: SizeInt): SizeInt;
+begin
+  if (Index > 0) and (Index <= Length(S)) then
+    for Result := Index to Length(S) do
+      if S[Result] = C then
+        Exit;
+  Result := 0;
+end;
+
+function CharIPos(const S: AnsiString; C: AnsiChar; const Index: SizeInt): SizeInt;
+begin
+  if (Index > 0) and (Index <= Length(S)) then
+  begin
+    C := CharUpper(C);
+    for Result := Index to Length(S) do
+      if AnsiCaseMap[Ord(S[Result]) + AnsiUpOffset] = C then
+        Exit;
+  end;
+  Result := 0;
+end;
+
+function CharReplace(var S: AnsiString; const Search, Replace: AnsiChar): SizeInt;
+var
+  P: PAnsiChar;
+  Index, Len: SizeInt;
+begin
+  Result := 0;
+  if Search <> Replace then
+  begin
+    UniqueString(S);
+    Len := Length(S);
+    P := PAnsiChar(S);
+    for Index := 0 to Len - 1 do
+    begin
+      if P^ = Search then
+      begin
+        P^ := Replace;
+        Inc(Result);
+      end;
+      Inc(P);
+    end;
+  end;
+end;
+
+//=== MultiSz ================================================================
+
+function StringsToMultiSz(var Dest: PAnsiMultiSz; const Source: TJclAnsiStrings): PAnsiMultiSz;
+var
+  I, TotalLength: SizeInt;
+  P: PAnsiMultiSz;
+begin
+  Assert(Source <> nil);
+  TotalLength := 1;
+  for I := 0 to Source.Count - 1 do
+    if Source[I] = '' then
+      raise EJclAnsiStringError.CreateRes(@RsInvalidEmptyStringItem)
+    else
+      Inc(TotalLength, StrLenA(PAnsiChar(AnsiString(Source[I]))) + 1);
+  AllocateMultiSz(Dest, TotalLength);
+  P := Dest;
+  for I := 0 to Source.Count - 1 do
+  begin
+    P := StrECopyA(P, PAnsiChar(AnsiString(Source[I])));
+    Inc(P);
+  end;
+  P^ := #0;
+  Result := Dest;
+end;
+
+procedure MultiSzToStrings(const Dest: TJclAnsiStrings; const Source: PAnsiMultiSz);
+var
+  P: PAnsiMultiSz;
+begin
+  Assert(Dest <> nil);
+  Dest.BeginUpdate;
+  try
+    Dest.Clear;
+    if Source <> nil then
+    begin
+      P := Source;
+      while P^ <> #0 do
+      begin
+        Dest.Add(P);
+        P := StrEndA(P);
+        Inc(P);
+      end;
+    end;
+  finally
+    Dest.EndUpdate;
+  end;
+end;
+
+function MultiSzLength(const Source: PAnsiMultiSz): SizeInt;
+var
+  P: PAnsiMultiSz;
+begin
+  Result := 0;
+  if Source <> nil then
+  begin
+    P := Source;
+    repeat
+      Inc(Result, StrLenA(P) + 1);
+      P := StrEndA(P);
+      Inc(P);
+    until P^ = #0;
+    Inc(Result);
+  end;
+end;
+
+procedure AllocateMultiSz(var Dest: PAnsiMultiSz; Len: SizeInt);
+begin
+  if Len > 0 then
+    GetMem(Dest, Len * SizeOf(AnsiChar))
+  else
+    Dest := nil;
+end;
+
+procedure FreeMultiSz(var Dest: PAnsiMultiSz);
+begin
+  if Dest <> nil then
+    FreeMem(Dest);
+  Dest := nil;
+end;
+
+function MultiSzDup(const Source: PAnsiMultiSz): PAnsiMultiSz;
+var
+  Len: SizeInt;
+begin
+  if Source <> nil then
+  begin
+    Len := MultiSzLength(Source);
+    Result := nil;
+    AllocateMultiSz(Result, Len);
+    Move(Source^, Result^, Len * SizeOf(AnsiChar));
+  end
+  else
+    Result := nil;
+end;
+
+//=== TJclAnsiStrings Manipulation ===============================================
+
+procedure StrToStrings(S, Sep: AnsiString; const List: TJclAnsiStrings; const AllowEmptyString: Boolean = True);
+var
+  I, L: SizeInt;
+  Left: AnsiString;
+begin
+  Assert(List <> nil);
+  List.BeginUpdate;
+  try
+    List.Clear;
+    L := Length(Sep);
+    I := Pos(Sep, S);
+    while I > 0 do
+    begin
+      Left := StrLeft(S, I - 1);
+      if (Left <> '') or AllowEmptyString then
+        List.Add(Left);
+      Delete(S, 1, I + L - 1);
+      I := Pos(Sep, S);
+    end;
+    if (S <> '') or AllowEmptyString then
+      List.Add(S);  // Ignore empty strings at the end (only if AllowEmptyString = False).
+  finally
+    List.EndUpdate;
+  end;
+end;
+
+procedure StrIToStrings(S, Sep: AnsiString; const List: TJclAnsiStrings; const AllowEmptyString: Boolean = True);
+var
+  I, L: SizeInt;
+  LowerCaseStr: AnsiString;
+  Left: AnsiString;
+begin
+  Assert(List <> nil);
+  LowerCaseStr := StrLower(S);
+  Sep := StrLower(Sep);
+  L := Length(Sep);
+  I := Pos(Sep, LowerCaseStr);
+  List.BeginUpdate;
+  try
+    List.Clear;
+    while I > 0 do
+    begin
+      Left := StrLeft(S, I - 1);
+      if (Left <> '') or AllowEmptyString then
+        List.Add(Left);
+      Delete(S, 1, I + L - 1);
+      Delete(LowerCaseStr, 1, I + L - 1);
+      I := Pos(Sep, LowerCaseStr);
+    end;
+    if (S <> '') or AllowEmptyString then
+      List.Add(S);  // Ignore empty strings at the end (only if AllowEmptyString = False).
+  finally
+    List.EndUpdate;
+  end;
+end;
+
+function StringsToStr(const List: TJclAnsiStrings; const Sep: AnsiString;
+  const AllowEmptyString: Boolean): AnsiString;
+var
+  I, L: SizeInt;
+begin
+  Result := '';
+  for I := 0 to List.Count - 1 do
+  begin
+    if (List[I] <> '') or AllowEmptyString then
+    begin
+      // don't combine these into one addition, somehow it hurts performance
+      Result := Result + List[I];
+      Result := Result + Sep;
+    end;
+  end;
+  // remove terminating separator
+  if List.Count <> 0 then
+  begin
+    L := Length(Sep);
+    Delete(Result, Length(Result) - L + 1, L);
+  end;
+end;
+
+procedure TrimStrings(const List: TJclAnsiStrings; DeleteIfEmpty: Boolean);
+var
+  I: SizeInt;
+begin
+  Assert(List <> nil);
+  List.BeginUpdate;
+  try
+    for I := List.Count - 1 downto 0 do
+    begin
+      List[I] := Trim(List[I]);
+      if (List[I] = '') and DeleteIfEmpty then
+        List.Delete(I);
+    end;
+  finally
+    List.EndUpdate;
+  end;
+end;
+
+procedure TrimStringsRight(const List: TJclAnsiStrings; DeleteIfEmpty: Boolean);
+var
+  I: SizeInt;
+begin
+  Assert(List <> nil);
+  List.BeginUpdate;
+  try
+    for I := List.Count - 1 downto 0 do
+    begin
+      List[I] := TrimRight(List[I]);
+      if (List[I] = '') and DeleteIfEmpty then
+        List.Delete(I);
+    end;
+  finally
+    List.EndUpdate;
+  end;
+end;
+
+procedure TrimStringsLeft(const List: TJclAnsiStrings; DeleteIfEmpty: Boolean);
+var
+  I: SizeInt;
+begin
+  Assert(List <> nil);
+  List.BeginUpdate;
+  try
+    for I := List.Count - 1 downto 0 do
+    begin
+      List[I] := TrimLeft(List[I]);
+      if (List[I] = '') and DeleteIfEmpty then
+        List.Delete(I);
+    end;
+  finally
+    List.EndUpdate;
+  end;
+end;
+
+function AddStringToStrings(const S: AnsiString; Strings: TJclAnsiStrings; const Unique: Boolean): Boolean;
+begin
+  Assert(Strings <> nil);
+  Result := Unique and (Strings.IndexOf(S) <> -1);
+  if not Result then
+    Result := Strings.Add(S) > -1;
+end;
+
+//=== Miscellaneous ==========================================================
+
+function FileToString(const FileName: TFileName): AnsiString;
+var
+  FS: TFileStream;
+  Len: SizeInt;
+begin
+  FS := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
+  try
+    Len := FS.Size;
+    SetLength(Result, Len);
+    if Len > 0 then
+    FS.ReadBuffer(Result[1], Len);
+  finally
+    FS.Free;
+  end;
+end;
+
+procedure StringToFile(const FileName: TFileName; const Contents: AnsiString; Append: Boolean);
+var
+  FS: TFileStream;
+  Len: SizeInt;
+begin
+  if Append and FileExists(FileName) then
+    FS := TFileStream.Create(FileName, fmOpenReadWrite or fmShareDenyWrite)
+  else
+    FS := TFileStream.Create(FileName, fmCreate);
+  try
+    if Append then
+      FS.Seek(0, soEnd);  // faster than .Position := .Size
+    Len := Length(Contents);
+    if Len > 0 then
+    FS.WriteBuffer(Contents[1], Len);
+  finally
+    FS.Free;
+  end;
+end;
+
+function StrToken(var S: AnsiString; Separator: AnsiChar): AnsiString;
+var
+  I: SizeInt;
+begin
+  I := Pos(Separator, S);
+  if I <> 0 then
+  begin
+    Result := Copy(S, 1, I - 1);
+    Delete(S, 1, I);
+  end
+  else
+  begin
+    Result := S;
+    S := '';
+  end;
+end;
+
+procedure StrTokens(const S: AnsiString; const List: TJclAnsiStrings);
+var
+  Start: PAnsiChar;
+  Token: AnsiString;
+  Done: Boolean;
+begin
+  Assert(List <> nil);
+  if List = nil then
+    Exit;
+
+  List.BeginUpdate;
+  try
+    List.Clear;
+    Start := Pointer(S);
+    repeat
+      Done := StrWord(Start, Token);
+      if Token <> '' then
+        List.Add(Token);
+    until Done;
+  finally
+    List.EndUpdate;
+  end;
+end;
+
+procedure StrTokenToStrings(S: AnsiString; Separator: AnsiChar; const List: TJclAnsiStrings);
+var
+  Token: AnsiString;
+begin
+  Assert(List <> nil);
+
+  if List = nil then
+    Exit;
+
+  List.BeginUpdate;
+  try
+    List.Clear;
+    while S <> '' do
+    begin
+      Token := StrToken(S, Separator);
+      List.Add(Token);
+    end;
+  finally
+    List.EndUpdate;
+  end;
+end;
+
+function StrWord(const S: AnsiString; var Index: SizeInt; out Word: AnsiString): Boolean;
+var
+  Start: SizeInt;
+  C: AnsiChar;
+begin
+  Word := '';
+  if (S = '') then
+  begin
+    Result := True;
+    Exit;
+  end;
+  Start := Index;
+  Result := False;
+  while True do
+  begin
+    C := S[Index];
+    case C of
+      #0:
+        begin
+          if Start <> 0 then
+            Word := Copy(S, Start, Index - Start);
+          Result := True;
+          Exit;
+        end;
+      AnsiSpace, AnsiLineFeed, AnsiCarriageReturn:
+        begin
+          if Start <> 0 then
+          begin
+            Word := Copy(S, Start, Index - Start);
+            Exit;
+          end
+          else
+          begin
+            while CharIsWhiteSpace(C) do
+            begin
+              Inc(Index);
+              C := S[Index];
+            end;
+          end;
+        end;
+    else
+      if Start = 0 then
+        Start := Index;
+      Inc(Index);
+    end;
+  end;
+end;
+
+function StrWord(var S: PAnsiChar; out Word: AnsiString): Boolean;
+var
+  Start: PAnsiChar;
+begin
+  Word := '';
+  if S = nil then
+  begin
+    Result := True;
+    Exit;
+  end;
+  Start := nil;
+  Result := False;
+  while True do
+  begin
+    case S^ of
+      #0:
+      begin
+        if Start <> nil then
+          SetString(Word, Start, S - Start);
+        Result := True;
+        Exit;
+      end;
+      AnsiSpace, AnsiLineFeed, AnsiCarriageReturn:
+      begin
+        if Start <> nil then
+        begin
+          SetString(Word, Start, S - Start);
+          Exit;
+        end
+        else
+          while CharIsWhiteSpace(S^) do
+            Inc(S);
+      end;
+    else
+      if Start = nil then
+        Start := S;
+      Inc(S);
+    end;
+  end;
+end;
+
+function StrIdent(const S: AnsiString; var Index: SizeInt; out Ident: AnsiString): Boolean;
+var
+  Start: SizeInt;
+  C: AnsiChar;
+begin
+  Ident := '';
+  if (S = '') then
+  begin
+    Result := True;
+    Exit;
+  end;
+  Start := Index;
+  Result := False;
+  while True do
+  begin
+    C := S[Index];
+    if CharIsValidIdentifierLetter(C) then
+    begin
+      if Start = 0 then
+        Start := Index;
+    end
+    else
+    if C = #0 then
+    begin
+      if Start <> 0 then
+        Ident := Copy(S, Start, Index - Start);
+      Result := True;
+      Exit;
+    end
+    else
+    begin
+      if Start <> 0 then
+      begin
+        Ident := Copy(S, Start, Index - Start);
+        Exit;
+      end;
+    end;
+    Inc(Index);
+  end;
+end;
+
+function StrIdent(var S: PAnsiChar; out Ident: AnsiString): Boolean;
+var
+  Start: PAnsiChar;
+  C: AnsiChar;
+begin
+  Ident := '';
+  if S = nil then
+  begin
+    Result := True;
+    Exit;
+  end;
+  Start := nil;
+  Result := False;
+  while True do
+  begin
+    C := S^;
+    if CharIsValidIdentifierLetter(C) then
+    begin
+      if Start = nil then
+        Start := S;
+    end
+    else
+    if C = #0 then
+    begin
+      if Start <> nil then
+        SetString(Ident, Start, S - Start);
+      Result := True;
+      Exit;
+    end
+    else
+    begin
+      if Start <> nil then
+      begin
+        SetString(Ident, Start, S - Start);
+        Exit;
+      end
+    end;
+    Inc(S);
+  end;
+end;
+
+function StrToFloatSafe(const S: AnsiString): Float;
+var
+  Temp: AnsiString;
+  I, J, K: SizeInt;
+  SwapSeparators, IsNegative: Boolean;
+  DecSep: AnsiChar;
+  ThouSep: AnsiChar;
+begin
+  DecSep := AnsiChar(JclFormatSettings.DecimalSeparator);
+  ThouSep := AnsiChar(JclFormatSettings.ThousandSeparator);
+  Temp := S;
+  SwapSeparators := False;
+
+  IsNegative := False;
+  J := 0;
+  for I := 1 to Length(Temp) do
+  begin
+    if Temp[I] = '-' then
+      IsNegative := not IsNegative
+    else
+    if not (Temp[I] in [' ', '(', '+']) then
+    begin
+      // if it appears prior to any digit, it has to be a decimal separator
+      SwapSeparators := Temp[I] = ThouSep;
+      J := I;
+      Break;
+    end;
+  end;
+
+  if not SwapSeparators then
+  begin
+    K := CharPos(Temp, DecSep);
+    SwapSeparators :=
+      // if it appears prior to any digit, it has to be a decimal separator
+      (K > J) and
+      // if it appears multiple times, it has to be a thousand separator
+      ((StrCharCount(Temp, DecSep) > 1) or
+      // we assume (consistent with Windows Platform SDK documentation),
+      // that thousand separators appear only to the left of the decimal
+      (K < CharPos(Temp, ThouSep)));
+  end;
+
+  if SwapSeparators then
+  begin
+    // assume a numerical string from a different locale,
+    // where DecimalSeparator and ThousandSeparator are exchanged
+    for I := 1 to Length(Temp) do
+      if Temp[I] = DecSep then
+        Temp[I] := ThouSep
+      else
+      if Temp[I] = ThouSep then
+        Temp[I] := DecSep;
+  end;
+
+  Temp := StrKeepChars(Temp, AnsiDecDigits + [DecSep]);
+
+  if Length(Temp) > 0 then
+  begin
+    if Temp[1] = DecSep then
+      Temp := '0' + Temp;
+    if Temp[Length(Temp)] = DecSep then
+      Temp := Temp + '0';
+    Result := StrToFloat(string(Temp));
+    if IsNegative then
+      Result := -Result;
+  end
+  else
+    Result := 0.0;
+end;
+
+function StrToIntSafe(const S: AnsiString): Integer;
+begin
+  Result := Trunc(StrToFloatSafe(S));
+end;
+
+procedure StrNormIndex(const StrLen: SizeInt; var Index: SizeInt; var Count: SizeInt); overload;
+begin
+  Index := Max(1, Min(Index, StrLen + 1));
+  Count := Max(0, Min(Count, StrLen + 1 - Index));
+end;
+
+function ArrayOf(List: TJclAnsiStrings): TDynStringArray;
+var
+  I: SizeInt;
+begin
+  if List <> nil then
+  begin
+    SetLength(Result, List.Count);
+    for I := 0 to List.Count - 1 do
+      Result[I] := string(List[I]);
+  end
+  else
+    Result := nil;
+end;
+
+function AnsiCompareNatural(const S1, S2: AnsiString; CaseInsensitive: Boolean): SizeInt;
+var
+  Cur1, Len1,
+  Cur2, Len2: SizeInt;
+
+  procedure NumberCompare;
+  var
+    IsReallyNumber: Boolean;
+    FirstDiffBreaks: Boolean;
+    Val1, Val2: SizeInt;
+  begin
+    Result := 0;
+    IsReallyNumber := False;
+    // count leading spaces in S1
+    while CharIsWhiteSpace(S1[Cur1]) do
+    begin
+      Dec(Result);
+      Inc(Cur1);
+    end;
+    // count leading spaces in S2 (canceling them out against the ones in S1)
+    while CharIsWhiteSpace(S2[Cur2]) do
+    begin
+      Inc(Result);
+      Inc(Cur2);
+    end;
+
+    // if spaces match, or both strings are actually followed by a numeric character, continue the checks
+    if (Result = 0) or (CharIsNumberChar(S1[Cur1])) and (CharIsNumberChar(S2[Cur2])) then
+    begin
+      // Check signed number
+      if (S1[Cur1] = '-') and (S2[Cur2] <> '-') then
+        Result := 1
+      else
+      if (S2[Cur2] = '-') and (S1[Cur1] <> '-') then
+        Result := -1
+      else
+        Result := 0;
+
+      if (S1[Cur1] = '-') or (S1[Cur1] = '+') then
+        Inc(Cur1);
+      if (S2[Cur2] = '-') or (S2[Cur2] = '+') then
+        Inc(Cur2);
+
+      FirstDiffBreaks := (S1[Cur1] = '0') or (S2[Cur2] = '0');
+      while CharIsDigit(S1[Cur1]) and CharIsDigit(S2[Cur2]) do
+      begin
+        IsReallyNumber := True;
+        Val1 := StrToInt(string(S1[Cur1]));
+        Val2 := StrToInt(string(S2[Cur2]));
+
+        if (Result = 0) and (Val1 < Val2) then
+          Result := -1
+        else
+        if (Result = 0) and (Val1 > Val2) then
+          Result := 1;
+        if FirstDiffBreaks and (Result <> 0) then
+          Break;
+        Inc(Cur1);
+        Inc(Cur2);
+      end;
+
+      if IsReallyNumber then
+      begin
+        if not FirstDiffBreaks then
+        begin
+          if CharIsDigit(S1[Cur1]) then
+            Result := 1
+          else
+          if CharIsDigit(S2[Cur2]) then
+            Result := -1;
+        end;
+      end;
+    end;
+  end;
+
+begin
+  Cur1 := 1;
+  Len1 := Length(S1);
+  Cur2 := 1;
+  Len2 := Length(S2);
+  Result := 0;
+
+  while (Result = 0) do
+  begin
+    if (Cur1 = Len1) and (Cur2 = Len2) then
+      Break
+    else
+    if (S1[Cur1] = '-') and CharIsNumberChar(S2[Cur2]) and (S2[Cur2] <> '-') then
+      Result := -1
+    else
+    if (S2[Cur2] = '-') and CharIsNumberChar(S1[Cur1]) and (S1[Cur1] <> '-') then
+      Result := 1
+    else
+    if CharIsNumberChar(S1[Cur1]) and CharIsNumberChar(S2[Cur2]) then
+      NumberCompare
+    else
+    if (Cur1 = Len1) and (Cur2 < Len2) then
+      Result := -1
+    else
+    if (Cur1 < Len1) and (Cur2 = Len2) then
+      Result := 1
+    else
+    begin
+      Result := StrCompare(S1,S2);
+      if CaseInsensitive then
+        Result := AnsiStrLICompA(PAnsiChar(@S1[Cur1]), PAnsiChar(@S2[Cur2]), 1)
+      else
+        Result := AnsiStrLCompA(PAnsiChar(@S1[Cur1]), PAnsiChar(@S2[Cur2]), 1);
+      Inc(Cur1);
+      Inc(Cur2);
+    end;
+  end;
+end;
+
+function AnsiCompareNaturalStr(const S1, S2: AnsiString): SizeInt; overload;
+begin
+  Result := AnsiCompareNatural(S1, S2, False);
+end;
+
+function AnsiCompareNaturalText(const S1, S2: AnsiString): SizeInt; overload;
+begin
+  Result := AnsiCompareNatural(S1, S2, True);
+end;
+
+
+function StrNewA(const Str: PAnsiChar): PAnsiChar;
+begin
+  Result := {$IFDEF DEPRECATED_SYSUTILS_ANSISTRINGS}System.AnsiStrings.{$ENDIF}StrNew(Str);
+end;
+
+procedure StrDisposeA(Str: PAnsiChar);
+begin
+  {$IFDEF DEPRECATED_SYSUTILS_ANSISTRINGS}System.AnsiStrings.{$ENDIF}StrDispose(Str);
+end;
+
+function StrLenA(S: PAnsiChar): Integer;
+begin
+  Result := {$IFDEF DEPRECATED_SYSUTILS_ANSISTRINGS}System.AnsiStrings.{$ENDIF}StrLen(S);
+end;
+
+function StrEndA(const Str: PAnsiChar): PAnsiChar;
+begin
+  Result := {$IFDEF DEPRECATED_SYSUTILS_ANSISTRINGS}System.AnsiStrings.{$ENDIF}StrEnd(Str);
+end;
+
+function StrPosA(const Str1, Str2: PAnsiChar): PAnsiChar;
+begin
+  Result := {$IFDEF DEPRECATED_SYSUTILS_ANSISTRINGS}System.AnsiStrings.{$ENDIF}StrPos(Str1, Str2);
+end;
+
+function StrPasA(const Str: PAnsiChar): AnsiString;
+begin
+  Result := {$IFDEF DEPRECATED_SYSUTILS_ANSISTRINGS}System.AnsiStrings.{$ENDIF}StrPas(Str);
+end;
+
+function StrCopyA(Dest: PAnsiChar; const Source: PAnsiChar): PAnsiChar;
+begin
+  Result := {$IFDEF DEPRECATED_SYSUTILS_ANSISTRINGS}System.AnsiStrings.{$ENDIF}StrCopy(Dest, Source);
+end;
+
+function StrLCopyA(Dest: PAnsiChar; const Source: PAnsiChar; MaxLen: Cardinal): PAnsiChar;
+begin
+  Result := {$IFDEF DEPRECATED_SYSUTILS_ANSISTRINGS}System.AnsiStrings.{$ENDIF}StrLCopy(Dest, Source, MaxLen);
+end;
+
+function StrPCopyA(Dest: PAnsiChar; const Source: AnsiString): PAnsiChar;
+begin
+  Result := {$IFDEF DEPRECATED_SYSUTILS_ANSISTRINGS}System.AnsiStrings.{$ENDIF}StrPCopy(Dest, Source);
+end;
+
+function StrPLCopyA(Dest: PAnsiChar; const Source: AnsiString; MaxLen: Cardinal): PAnsiChar;
+begin
+  Result := {$IFDEF DEPRECATED_SYSUTILS_ANSISTRINGS}System.AnsiStrings.{$ENDIF}StrPLCopy(Dest, Source, MaxLen);
+end;
+
+function StrECopyA(Dest: PAnsiChar; const Source: PAnsiChar): PAnsiChar;
+begin
+  Result := {$IFDEF DEPRECATED_SYSUTILS_ANSISTRINGS}System.AnsiStrings.{$ENDIF}StrECopy(Dest, Source);
+end;
+
+function StrCatA(Dest: PAnsiChar; const Source: PAnsiChar): PAnsiChar;
+begin
+  Result := {$IFDEF DEPRECATED_SYSUTILS_ANSISTRINGS}System.AnsiStrings.{$ENDIF}StrCat(Dest, Source);
+end;
+
+function StrLCatA(Dest: PAnsiChar; const Source: PAnsiChar; MaxLen: Cardinal): PAnsiChar;
+begin
+  Result := {$IFDEF DEPRECATED_SYSUTILS_ANSISTRINGS}System.AnsiStrings.{$ENDIF}StrLCat(Dest, Source, MaxLen);
+end;
+
+function StrCompA(const Str1, Str2: PAnsiChar): Integer;
+begin
+  Result := {$IFDEF DEPRECATED_SYSUTILS_ANSISTRINGS}System.AnsiStrings.{$ENDIF}StrComp(Str1, Str2);
+end;
+
+function StrLCompA(const Str1, Str2: PAnsiChar; MaxLen: Cardinal): Integer;
+begin
+  Result := {$IFDEF DEPRECATED_SYSUTILS_ANSISTRINGS}System.AnsiStrings.{$ENDIF}StrLComp(Str1, Str2, MaxLen);
+end;
+
+function StrICompA(const Str1, Str2: PAnsiChar): Integer;
+begin
+  Result := {$IFDEF DEPRECATED_SYSUTILS_ANSISTRINGS}System.AnsiStrings.{$ENDIF}StrIComp(Str1, Str2);
+end;
+
+function StrLICompA(const Str1, Str2: PAnsiChar; MaxLen: Cardinal): Integer;
+begin
+  Result := {$IFDEF DEPRECATED_SYSUTILS_ANSISTRINGS}System.AnsiStrings.{$ENDIF}StrIComp(Str1, Str2);
+end;
+
+function StrFmtA(Buffer, Format: PAnsiChar; const Args: array of const): PAnsiChar;
+begin
+  Result := {$IFDEF DEPRECATED_SYSUTILS_ANSISTRINGS}System.AnsiStrings.{$ENDIF}StrFmt(Buffer, Format, Args);
+end;
+
+function AnsiStrPosA(const Str1, Str2: PAnsiChar): PAnsiChar;
+begin
+  Result := {$IFDEF DEPRECATED_SYSUTILS_ANSISTRINGS}System.AnsiStrings.{$ENDIF}AnsiStrPos(Str1, Str2);
+end;
+
+function AnsiStrLICompA(S1, S2: PAnsiChar; MaxLen: Cardinal): Integer;
+begin
+  Result := {$IFDEF DEPRECATED_SYSUTILS_ANSISTRINGS}System.AnsiStrings.{$ENDIF}AnsiStrLIComp(S1, S2, MaxLen);
+end;
+
+function AnsiStrLCompA(S1, S2: PAnsiChar; MaxLen: Cardinal): Integer;
+begin
+  Result := {$IFDEF DEPRECATED_SYSUTILS_ANSISTRINGS}System.AnsiStrings.{$ENDIF}AnsiStrLComp(S1, S2, MaxLen);
+end;
+
+
+initialization
+  LoadCharTypes;  // this table first
+  LoadCaseMap;    // or this function does not work
+{$IFDEF UNITVERSIONING}
+  RegisterUnitVersion(HInstance, UnitVersioning);
+
+finalization
+  UnregisterUnitVersion(HInstance);
+{$ENDIF UNITVERSIONING}
+
+end.

+ 1762 - 0
source/packages/jcl/JclTD32.pas

@@ -0,0 +1,1762 @@
+{**************************************************************************************************}
+{                                                                                                  }
+{ Project JEDI Code Library (JCL)                                                                  }
+{                                                                                                  }
+{ The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); }
+{ you may not use this file except in compliance with the License. You may obtain a copy of the    }
+{ License at http://www.mozilla.org/MPL/                                                           }
+{                                                                                                  }
+{ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF   }
+{ ANY KIND, either express or implied. See the License for the specific language governing rights  }
+{ and limitations under the License.                                                               }
+{                                                                                                  }
+{ The Original Code is JclTD32.pas.                                                                }
+{                                                                                                  }
+{ The Initial Developer of the Original Code is Flier Lu (<flier_lu att yahoo dott com dott cn>).  }
+{ Portions created by Flier Lu are Copyright (C) Flier Lu.  All Rights Reserved.                   }
+{                                                                                                  }
+{ Contributors:                                                                                    }
+{   Flier Lu (flier)                                                                               }
+{   Olivier Sannier (obones)                                                                       }
+{   Petr Vones (pvones)                                                                            }
+{   Heinz Zastrau (heinzz)                                                                         }
+{   Andreas Hausladen (ahuser)                                                                     }
+{                                                                                                  }
+{**************************************************************************************************}
+{                                                                                                  }
+{ Borland TD32 symbolic debugging information support routines and classes.                        }
+{                                                                                                  }
+{**************************************************************************************************}
+{                                                                                                  }
+{ Last modified: $Date::                                                                         $ }
+{ Revision:      $Rev::                                                                          $ }
+{ Author:        $Author::                                                                       $ }
+{                                                                                                  }
+{**************************************************************************************************}
+
+unit JclTD32;
+
+interface
+
+{$I jcl.inc}
+{$I windowsonly.inc}
+
+uses
+  {$IFDEF UNITVERSIONING}
+  JclUnitVersioning,
+  {$ENDIF UNITVERSIONING}
+  {$IFDEF HAS_UNITSCOPE}
+  {$IFDEF MSWINDOWS}
+  Winapi.Windows,
+  {$ENDIF MSWINDOWS}
+  System.Classes, System.SysUtils, System.Contnrs,
+  {$ELSE ~HAS_UNITSCOPE}
+  {$IFDEF MSWINDOWS}
+  Windows,
+  {$ENDIF MSWINDOWS}
+  Classes, SysUtils, Contnrs,
+  {$ENDIF ~HAS_UNITSCOPE}
+  JclBase,
+  {$IFDEF BORLAND}
+  JclPeImage,
+  {$ENDIF BORLAND}
+  JclFileUtils;
+
+{ TODO -cDOC : Original code: "Flier Lu" <flier_lu att yahoo dott com dott cn> }
+
+// TD32 constants and structures
+{*******************************************************************************
+
+  [-----------------------------------------------------------------------]
+  [         Symbol and Type OMF Format Borland Executable Files           ]
+  [-----------------------------------------------------------------------]
+
+  Introduction
+
+  This section describes the format used to embed debugging information  into
+  the executable file.
+
+  Debug Information Format
+
+  The format encompasses a block of  data which goes at  the end of the  .EXE
+  file,  i.e.,  after   the   header   plus   load   image,   overlays,   and
+  Windows/Presentation Manager  resource  compiler  information.   The  lower
+  portion of the file is unaffected by the additional data.
+
+  The last eight bytes of the file contain a signature and a long file offset
+  from the end of the file (lfoBase).  The signature is FBxx, where xx is the
+  version number.  The  long  offset  indicates  the  position  in  the  file
+  (relative to the end of the file)  of the base address.  For the LX  format
+  executables, the base address  is determined by  looking at the  executable
+  header.
+
+  The signatures have the following meanings:
+
+    FB09        The signature for a Borland 32 bit symbol file.
+
+  The value
+
+          lfaBase=length of the file - lfoBase
+
+  gives the base address of the start of the Symbol and Type OMF  information
+  relative to  the beginning  of the  file.  All  other file  offsets in  the
+  Symbol and Type OMF are relative to  the lfaBase.  At the base address  the
+  signature is repeated, followed by the long displacement to the  subsection
+  directory (lfoDir).  All subsections start on a long word boundary and  are
+  designed to maintain  natural alignment internally  in each subsection  and
+  within the subsection directory.
+
+  Subsection Directory
+
+  The subsection directory has the format
+
+       Directory header
+
+       Directory entry 0
+
+       Directory entry 1
+
+        .
+        .
+        .
+
+       Directory entry n
+
+  There is no requirement for a particular subsection of a particular module to exist.
+
+  The following is the layout of the FB09 debug information in the image:
+
+  FB09 Header
+
+    sstModule [1]
+    .
+    .
+    .
+    sstModule [n]
+
+    sstAlignSym [1]
+    sstSrcModule [1]
+    .
+    .
+    .
+    sstAlignSym [n]
+    sstSrcModule [n]
+
+    sstGlobalSym
+    sstGlobalTypes
+    sstNames
+
+    SubSection Directory
+
+  FB09 Trailer
+
+*******************************************************************************}
+
+const
+  Borland32BitSymbolFileSignatureForDelphi = $39304246; // 'FB09'
+  Borland32BitSymbolFileSignatureForBCB    = $41304246; // 'FB0A'
+
+type
+  { Signature structure }
+  PJclTD32FileSignature = ^TJclTD32FileSignature;
+  TJclTD32FileSignature = packed record
+    Signature: DWORD;
+    Offset: DWORD;
+  end;
+
+const
+  { Subsection Types }
+  SUBSECTION_TYPE_MODULE         = $120;
+  SUBSECTION_TYPE_TYPES          = $121;
+  SUBSECTION_TYPE_SYMBOLS        = $124;
+  SUBSECTION_TYPE_ALIGN_SYMBOLS  = $125;
+  SUBSECTION_TYPE_SOURCE_MODULE  = $127;
+  SUBSECTION_TYPE_GLOBAL_SYMBOLS = $129;
+  SUBSECTION_TYPE_GLOBAL_TYPES   = $12B;
+  SUBSECTION_TYPE_NAMES          = $130;
+
+type
+  { Subsection directory header structure }
+  { The directory header structure is followed by the directory entries
+    which specify the subsection type, module index, file offset, and size.
+    The subsection directory gives the location (LFO) and size of each subsection,
+    as well as its type and module number if applicable. }
+  PDirectoryEntry = ^TDirectoryEntry;
+  TDirectoryEntry = packed record
+    SubsectionType: Word; // Subdirectory type
+    ModuleIndex: Word;    // Module index
+    Offset: DWORD;        // Offset from the base offset lfoBase
+    Size: DWORD;          // Number of bytes in subsection
+  end;
+
+  { The subsection directory is prefixed with a directory header structure
+    indicating size and number of subsection directory entries that follow. }
+  PDirectoryHeader = ^TDirectoryHeader;
+  TDirectoryHeader = packed record
+    Size: Word;           // Length of this structure
+    DirEntrySize: Word;   // Length of each directory entry
+    DirEntryCount: DWORD; // Number of directory entries
+    lfoNextDir: DWORD;    // Offset from lfoBase of next directory.
+    Flags: DWORD;         // Flags describing directory and subsection tables.
+    DirEntries: array [0..0] of TDirectoryEntry;
+  end;
+
+
+{*******************************************************************************
+
+  SUBSECTION_TYPE_MODULE $120
+
+  This describes the basic information about an object module including  code
+  segments, module name,  and the  number of  segments for  the modules  that
+  follow.  Directory entries for  sstModules  precede  all  other  subsection
+  directory entries.
+
+*******************************************************************************}
+
+type
+  PSegmentInfo = ^TSegmentInfo;
+  TSegmentInfo = packed record
+    Segment: Word; // Segment that this structure describes
+    Flags: Word;   // Attributes for the logical segment.
+                   // The following attributes are defined:
+                   //   $0000  Data segment
+                   //   $0001  Code segment
+    Offset: DWORD; // Offset in segment where the code starts
+    Size: DWORD;   // Count of the number of bytes of code in the segment
+  end;
+  PSegmentInfoArray = ^TSegmentInfoArray;
+  TSegmentInfoArray = array [0..32767] of TSegmentInfo;
+
+  PModuleInfo = ^TModuleInfo;
+  TModuleInfo = packed record
+    OverlayNumber: Word;  // Overlay number
+    LibraryIndex: Word;   // Index into sstLibraries subsection
+                          // if this module was linked from a library
+    SegmentCount: Word;   // Count of the number of code segments
+                          // this module contributes to
+    DebuggingStyle: Word; // Debugging style  for this  module.
+    NameIndex: DWORD;     // Name index of module.
+    TimeStamp: DWORD;     // Time stamp from the OBJ file.
+    Reserved: array [0..2] of DWORD; // Set to 0.
+    Segments: array [0..0] of TSegmentInfo;
+                          // Detailed information about each segment
+                          // that code is contributed to.
+                          // This is an array of cSeg count segment
+                          // information descriptor structures.
+  end;
+
+{*******************************************************************************
+
+  SUBSECTION_TYPE_SOURCE_MODULE $0127
+
+  This table describes the source line number to addressing mapping
+  information for a module. The table permits the description of a module
+  containing multiple source files with each source file contributing code to
+  one or more code segments. The base addresses of the tables described
+  below are all relative to the beginning of the sstSrcModule table.
+
+
+  Module header
+
+  Information for source file 1
+
+    Information for segment 1
+         .
+         .
+         .
+    Information for segment n
+
+         .
+         .
+         .
+
+  Information for source file n
+
+    Information for segment 1
+         .
+         .
+         .
+    Information for segment n
+
+*******************************************************************************}
+type
+  { The line number to address mapping information is contained in a table with
+    the following format: }
+  PLineMappingEntry = ^TLineMappingEntry;
+  TLineMappingEntry = packed record
+    SegmentIndex: Word;  // Segment index for this table
+    PairCount: Word;     // Count of the number of source line pairs to follow
+    Offsets: array [0..0] of DWORD;
+                     // An array of 32-bit offsets for the offset
+                     // within the code segment ofthe start of ine contained
+                     // in the parallel array linenumber.
+    (*
+    { This is an array of 16-bit line numbers of the lines in the source file
+      that cause code to be emitted to the code segment.
+      This array is parallel to the offset array.
+      If cPair is not even, then a zero word is emitted to
+      maintain natural alignment in the sstSrcModule table. }
+    LineNumbers: array [0..PairCount - 1] of Word;
+    *)
+  end;
+
+  TOffsetPair = packed record
+    StartOffset: DWORD;
+    EndOffset: DWORD;
+  end;
+  POffsetPairArray = ^TOffsetPairArray;
+  TOffsetPairArray = array [0..32767] of TOffsetPair;
+
+  { The file table describes the code segments that receive code from this
+    source file. Source file entries have the following format: }
+  PSourceFileEntry = ^TSourceFileEntry;
+  TSourceFileEntry = packed record
+    SegmentCount: Word; // Number of segments that receive code from this source file.
+    NameIndex: DWORD;   // Name index of Source file name.
+
+    BaseSrcLines: array [0..0] of DWORD;
+                        // An array of offsets for the line/address mapping
+                        // tables for each of the segments that receive code
+                        // from this source file.
+    (*
+    { An array  of two  32-bit offsets  per segment  that
+      receives code from this  module.  The first  offset
+      is the offset within the segment of the first  byte
+      of code from this module.  The second offset is the
+      ending address of the  code from this module.   The
+      order of these pairs corresponds to the ordering of
+      the segments in the  seg  array.   Zeros  in  these
+      entries means that the information is not known and
+      the file and line tables described below need to be
+      examined to determine if an address of interest  is
+      contained within the code from this module. }
+    SegmentAddress: array [0..SegmentCount - 1] of TOffsetPair;
+
+    Name: ShortString; // Count of the number of bytes in source file name
+    *)
+  end;
+
+  { The module header structure describes the source file and code segment
+    organization of the module. Each module header has the following format: }
+  PSourceModuleInfo = ^TSourceModuleInfo;
+  TSourceModuleInfo = packed record
+    FileCount: Word;    // The number of source file scontributing code to segments
+    SegmentCount: Word; // The number of code segments receiving code from this module
+
+    BaseSrcFiles: array [0..0] of DWORD;
+    (*
+    // This is an array of base offsets from the beginning of the sstSrcModule table
+    BaseSrcFiles: array [0..FileCount - 1] of DWORD;
+
+    { An array  of two  32-bit offsets  per segment  that
+      receives code from this  module.  The first  offset
+      is the offset within the segment of the first  byte
+      of code from this module.  The second offset is the
+      ending address of the  code from this module.   The
+      order of these pairs corresponds to the ordering of
+      the segments in the  seg  array.   Zeros  in  these
+      entries means that the information is not known and
+      the file and line tables described below need to be
+      examined to determine if an address of interest  is
+      contained within the code from this module. }
+    SegmentAddress: array [0..SegmentCount - 1] of TOffsetPair;
+
+    { An array of segment indices that receive code  from
+      this module.  If the  number  of  segments  is  not
+      even, a pad word  is inserted  to maintain  natural
+      alignment. }
+    SegmentIndexes: array [0..SegmentCount - 1] of Word;
+    *)
+  end;
+
+{*******************************************************************************
+
+  SUBSECTION_TYPE_GLOBAL_TYPES $12b
+
+  This subsection contains the packed  type records for the executable  file.
+  The first long word of the subsection  contains the number of types in  the
+  table.  This count is  followed by a count-sized  array of long offsets  to
+  the  corresponding  type  record.   As  the  sstGlobalTypes  subsection  is
+  written, each  type record  is forced  to start  on a  long word  boundary.
+  However, the length of the  type string is NOT  adjusted by the pad  count.
+  The remainder of the subsection contains  the type records.
+
+*******************************************************************************}
+
+type
+  PGlobalTypeInfo = ^TGlobalTypeInfo;
+  TGlobalTypeInfo = packed record
+    Count: DWORD; // count of the number of types
+    // offset of each type string from the beginning of table
+    Offsets: array [0..0] of DWORD;
+  end;
+
+const
+  { Symbol type defines }
+  SYMBOL_TYPE_COMPILE        = $0001; // Compile flags symbol
+  SYMBOL_TYPE_REGISTER       = $0002; // Register variable
+  SYMBOL_TYPE_CONST          = $0003; // Constant symbol
+  SYMBOL_TYPE_UDT            = $0004; // User-defined Type
+  SYMBOL_TYPE_SSEARCH        = $0005; // Start search
+  SYMBOL_TYPE_END            = $0006; // End block, procedure, with, or thunk
+  SYMBOL_TYPE_SKIP           = $0007; // Skip - Reserve symbol space
+  SYMBOL_TYPE_CVRESERVE      = $0008; // Reserved for Code View internal use
+  SYMBOL_TYPE_OBJNAME        = $0009; // Specify name of object file
+
+  SYMBOL_TYPE_BPREL16        = $0100; // BP relative 16:16
+  SYMBOL_TYPE_LDATA16        = $0101; // Local data 16:16
+  SYMBOL_TYPE_GDATA16        = $0102; // Global data 16:16
+  SYMBOL_TYPE_PUB16          = $0103; // Public symbol 16:16
+  SYMBOL_TYPE_LPROC16        = $0104; // Local procedure start 16:16
+  SYMBOL_TYPE_GPROC16        = $0105; // Global procedure start 16:16
+  SYMBOL_TYPE_THUNK16        = $0106; // Thunk start 16:16
+  SYMBOL_TYPE_BLOCK16        = $0107; // Block start 16:16
+  SYMBOL_TYPE_WITH16         = $0108; // With start 16:16
+  SYMBOL_TYPE_LABEL16        = $0109; // Code label 16:16
+  SYMBOL_TYPE_CEXMODEL16     = $010A; // Change execution model 16:16
+  SYMBOL_TYPE_VFTPATH16      = $010B; // Virtual function table path descriptor 16:16
+
+  SYMBOL_TYPE_BPREL32        = $0200; // BP relative 16:32
+  SYMBOL_TYPE_LDATA32        = $0201; // Local data 16:32
+  SYMBOL_TYPE_GDATA32        = $0202; // Global data 16:32
+  SYMBOL_TYPE_PUB32          = $0203; // Public symbol 16:32
+  SYMBOL_TYPE_LPROC32        = $0204; // Local procedure start 16:32
+  SYMBOL_TYPE_GPROC32        = $0205; // Global procedure start 16:32
+  SYMBOL_TYPE_THUNK32        = $0206; // Thunk start 16:32
+  SYMBOL_TYPE_BLOCK32        = $0207; // Block start 16:32
+  SYMBOL_TYPE_WITH32         = $0208; // With start 16:32
+  SYMBOL_TYPE_LABEL32        = $0209; // Label 16:32
+  SYMBOL_TYPE_CEXMODEL32     = $020A; // Change execution model 16:32
+  SYMBOL_TYPE_VFTPATH32      = $020B; // Virtual function table path descriptor 16:32
+
+{*******************************************************************************
+
+  Global and Local Procedure Start 16:32
+
+  SYMBOL_TYPE_LPROC32 $0204
+  SYMBOL_TYPE_GPROC32 $0205
+
+    The symbol records define local (file static) and global procedure
+  definition. For C/C++, functions that are declared static to a module are
+  emitted as Local Procedure symbols. Functions not specifically declared
+  static are emitted as Global Procedures.
+    For each SYMBOL_TYPE_GPROC32 emitted, an SYMBOL_TYPE_GPROCREF symbol
+  must be fabricated and emitted to the SUBSECTION_TYPE_GLOBAL_SYMBOLS section.
+
+*******************************************************************************}
+
+type
+  TSymbolProcInfo = packed record
+    pParent: DWORD;
+    pEnd: DWORD;
+    pNext: DWORD;
+    Size: DWORD;        // Length in bytes of this procedure
+    DebugStart: DWORD;  // Offset in bytes from the start of the procedure to
+                        // the point where the stack frame has been set up.
+    DebugEnd: DWORD;    // Offset in bytes from the start of the procedure to
+                        // the point where the  procedure is  ready to  return
+                        // and has calculated its return value, if any.
+                        // Frame and register variables an still be viewed.
+    Offset: DWORD;      // Offset portion of  the segmented address of
+                        // the start of the procedure in the code segment
+    Segment: Word;      // Segment portion of the segmented address of
+                        // the start of the procedure in the code segment
+    ProcType: DWORD;    // Type of the procedure type record
+    NearFar: Byte;      // Type of return the procedure makes:
+                        //   0       near
+                        //   4       far
+    Reserved: Byte;
+    NameIndex: DWORD;   // Name index of procedure
+  end;
+
+  TSymbolObjNameInfo = packed record
+    Signature: DWORD;   // Signature for the CodeView information contained in
+                        // this module
+    NameIndex: DWORD;   // Name index of the object file
+  end;
+
+  TSymbolDataInfo = packed record
+    Offset: DWORD;      // Offset portion of  the segmented address of
+                        // the start of the data in the code segment
+    Segment: Word;      // Segment portion of the segmented address of
+                        // the start of the data in the code segment
+    Reserved: Word;
+    TypeIndex: DWORD;   // Type index of the symbol
+    NameIndex: DWORD;   // Name index of the symbol
+  end;
+
+  TSymbolWithInfo = packed record
+    pParent: DWORD;
+    pEnd: DWORD;
+    Size: DWORD;        // Length in bytes of this "with"
+    Offset: DWORD;      // Offset portion of the segmented address of
+                        // the start of the "with" in the code segment
+    Segment: Word;      // Segment portion of the segmented address of
+                        // the start of the "with" in the code segment
+    Reserved: Word;
+    NameIndex: DWORD;   // Name index of the "with"
+  end;
+
+  TSymbolLabelInfo = packed record
+    Offset: DWORD;      // Offset portion of  the segmented address of
+                        // the start of the label in the code segment
+    Segment: Word;      // Segment portion of the segmented address of
+                        // the start of the label in the code segment
+    NearFar: Byte;      // Address mode of the label:
+                        //   0       near
+                        //   4       far
+    Reserved: Byte;
+    NameIndex: DWORD;   // Name index of the label
+  end;
+
+  TSymbolConstantInfo = packed record
+    TypeIndex: DWORD;   // Type index of the constant (for enums)
+    NameIndex: DWORD;   // Name index of the constant
+    Reserved: DWORD;
+    Value: DWORD;       // value of the constant
+  end;
+
+  TSymbolUdtInfo = packed record
+    TypeIndex: DWORD;   // Type index of the type
+    Properties: Word;   // isTag:1 True if this is a tag (not a typedef)
+                        // isNest:1 True if the type is a nested type (its name
+                        // will be 'class_name::type_name' in that case)
+    NameIndex: DWORD;   // Name index of the type
+    Reserved: DWORD;
+  end;
+
+  TSymbolVftPathInfo = packed record
+    Offset: DWORD;      // Offset portion of start of the virtual function table
+    Segment: Word;      // Segment portion of the virtual function table
+    Reserved: Word;
+    RootIndex: DWORD;   // The type index of the class at the root of the path
+    PathIndex: DWORD;   // Type index of the record describing the base class
+                        // path from the root to the leaf class for the virtual
+                        // function table
+  end;
+
+type
+  { Symbol Information Records }
+  PSymbolInfo = ^TSymbolInfo;
+  TSymbolInfo = packed record
+    Size: Word;
+    SymbolType: Word;
+    case Word of
+      SYMBOL_TYPE_LPROC32, SYMBOL_TYPE_GPROC32:
+        (Proc: TSymbolProcInfo);
+      SYMBOL_TYPE_OBJNAME:
+        (ObjName: TSymbolObjNameInfo);
+      SYMBOL_TYPE_LDATA32, SYMBOL_TYPE_GDATA32, SYMBOL_TYPE_PUB32:
+        (Data: TSymbolDataInfo);
+      SYMBOL_TYPE_WITH32:
+        (With32: TSymbolWithInfo);
+      SYMBOL_TYPE_LABEL32:
+        (Label32: TSymbolLabelInfo);
+      SYMBOL_TYPE_CONST:
+        (Constant: TSymbolConstantInfo);
+      SYMBOL_TYPE_UDT:
+        (Udt: TSymbolUdtInfo);
+      SYMBOL_TYPE_VFTPATH32:
+        (VftPath: TSymbolVftPathInfo);
+  end;
+
+  PSymbolInfos = ^TSymbolInfos;
+  TSymbolInfos = packed record
+    Signature: DWORD;
+    Symbols: array [0..0] of TSymbolInfo;
+  end;
+
+{$IFDEF SUPPORTS_EXTSYM}
+
+{$EXTERNALSYM Borland32BitSymbolFileSignatureForDelphi}
+{$EXTERNALSYM Borland32BitSymbolFileSignatureForBCB}
+
+{$EXTERNALSYM SUBSECTION_TYPE_MODULE}
+{$EXTERNALSYM SUBSECTION_TYPE_TYPES}
+{$EXTERNALSYM SUBSECTION_TYPE_SYMBOLS}
+{$EXTERNALSYM SUBSECTION_TYPE_ALIGN_SYMBOLS}
+{$EXTERNALSYM SUBSECTION_TYPE_SOURCE_MODULE}
+{$EXTERNALSYM SUBSECTION_TYPE_GLOBAL_SYMBOLS}
+{$EXTERNALSYM SUBSECTION_TYPE_GLOBAL_TYPES}
+{$EXTERNALSYM SUBSECTION_TYPE_NAMES}
+
+{$EXTERNALSYM SYMBOL_TYPE_COMPILE}
+{$EXTERNALSYM SYMBOL_TYPE_REGISTER}
+{$EXTERNALSYM SYMBOL_TYPE_CONST}
+{$EXTERNALSYM SYMBOL_TYPE_UDT}
+{$EXTERNALSYM SYMBOL_TYPE_SSEARCH}
+{$EXTERNALSYM SYMBOL_TYPE_END}
+{$EXTERNALSYM SYMBOL_TYPE_SKIP}
+{$EXTERNALSYM SYMBOL_TYPE_CVRESERVE}
+{$EXTERNALSYM SYMBOL_TYPE_OBJNAME}
+
+{$EXTERNALSYM SYMBOL_TYPE_BPREL16}
+{$EXTERNALSYM SYMBOL_TYPE_LDATA16}
+{$EXTERNALSYM SYMBOL_TYPE_GDATA16}
+{$EXTERNALSYM SYMBOL_TYPE_PUB16}
+{$EXTERNALSYM SYMBOL_TYPE_LPROC16}
+{$EXTERNALSYM SYMBOL_TYPE_GPROC16}
+{$EXTERNALSYM SYMBOL_TYPE_THUNK16}
+{$EXTERNALSYM SYMBOL_TYPE_BLOCK16}
+{$EXTERNALSYM SYMBOL_TYPE_WITH16}
+{$EXTERNALSYM SYMBOL_TYPE_LABEL16}
+{$EXTERNALSYM SYMBOL_TYPE_CEXMODEL16}
+{$EXTERNALSYM SYMBOL_TYPE_VFTPATH16}
+
+{$EXTERNALSYM SYMBOL_TYPE_BPREL32}
+{$EXTERNALSYM SYMBOL_TYPE_LDATA32}
+{$EXTERNALSYM SYMBOL_TYPE_GDATA32}
+{$EXTERNALSYM SYMBOL_TYPE_PUB32}
+{$EXTERNALSYM SYMBOL_TYPE_LPROC32}
+{$EXTERNALSYM SYMBOL_TYPE_GPROC32}
+{$EXTERNALSYM SYMBOL_TYPE_THUNK32}
+{$EXTERNALSYM SYMBOL_TYPE_BLOCK32}
+{$EXTERNALSYM SYMBOL_TYPE_WITH32}
+{$EXTERNALSYM SYMBOL_TYPE_LABEL32}
+{$EXTERNALSYM SYMBOL_TYPE_CEXMODEL32}
+{$EXTERNALSYM SYMBOL_TYPE_VFTPATH32}
+
+{$ENDIF SUPPORTS_EXTSYM}
+
+// TD32 information related classes
+type
+  TJclTD32ModuleInfo = class(TObject)
+  private
+    FNameIndex: DWORD;
+    FSegments: PSegmentInfoArray;
+    FSegmentCount: Integer;
+    function GetSegment(const Idx: Integer): TSegmentInfo;
+  public
+    constructor Create(pModInfo: PModuleInfo);
+    property NameIndex: DWORD read FNameIndex;
+    property SegmentCount: Integer read FSegmentCount; //GetSegmentCount;
+    property Segment[const Idx: Integer]: TSegmentInfo read GetSegment; default;
+  end;
+
+  TJclTD32LineInfo = class(TObject)
+  private
+    FLineNo: DWORD;
+    FOffset: DWORD;
+  public
+    constructor Create(ALineNo, AOffset: DWORD);
+    property LineNo: DWORD read FLineNo;
+    property Offset: DWORD read FOffset;
+  end;
+
+  TJclTD32SourceModuleInfo = class(TObject)
+  private
+    FLines: TObjectList;
+    FSegments: POffsetPairArray;
+    FSegmentCount: Integer;
+    FNameIndex: DWORD;
+    function GetLine(const Idx: Integer): TJclTD32LineInfo;
+    function GetLineCount: Integer;
+    function GetSegment(const Idx: Integer): TOffsetPair;
+  public
+    constructor Create(pSrcFile: PSourceFileEntry; Base: TJclAddr);
+    destructor Destroy; override;
+    function FindLine(const AAddr: DWORD; out ALine: TJclTD32LineInfo): Boolean;
+    property NameIndex: DWORD read FNameIndex;
+    property LineCount: Integer read GetLineCount;
+    property Line[const Idx: Integer]: TJclTD32LineInfo read GetLine; default;
+    property SegmentCount: Integer read FSegmentCount; //GetSegmentCount;
+    property Segment[const Idx: Integer]: TOffsetPair read GetSegment;
+  end;
+
+  TJclTD32SymbolInfo = class(TObject)
+  private
+    FSymbolType: Word;
+  public
+    constructor Create(pSymInfo: PSymbolInfo); virtual;
+    property SymbolType: Word read FSymbolType;
+  end;
+
+  TJclTD32ProcSymbolInfo = class(TJclTD32SymbolInfo)
+  private
+    FNameIndex: DWORD;
+    FOffset: DWORD;
+    FSize: DWORD;
+  public
+    constructor Create(pSymInfo: PSymbolInfo); override;
+    property NameIndex: DWORD read FNameIndex;
+    property Offset: DWORD read FOffset;
+    property Size: DWORD read FSize;
+  end;
+
+  TJclTD32LocalProcSymbolInfo = class(TJclTD32ProcSymbolInfo);
+  TJclTD32GlobalProcSymbolInfo = class(TJclTD32ProcSymbolInfo);
+
+  { not used by Delphi }
+  TJclTD32ObjNameSymbolInfo = class(TJclTD32SymbolInfo)
+  private
+    FSignature: DWORD;
+    FNameIndex: DWORD;
+  public
+    constructor Create(pSymInfo: PSymbolInfo); override;
+    property NameIndex: DWORD read FNameIndex;
+    property Signature: DWORD read FSignature;
+  end;
+
+  TJclTD32DataSymbolInfo = class(TJclTD32SymbolInfo)
+  private
+    FOffset: DWORD;
+    FTypeIndex: DWORD;
+    FNameIndex: DWORD;
+  public
+    constructor Create(pSymInfo: PSymbolInfo); override;
+    property NameIndex: DWORD read FNameIndex;
+    property TypeIndex: DWORD read FTypeIndex;
+    property Offset: DWORD read FOffset;
+  end;
+
+  TJclTD32LDataSymbolInfo = class(TJclTD32DataSymbolInfo);
+  TJclTD32GDataSymbolInfo = class(TJclTD32DataSymbolInfo);
+  TJclTD32PublicSymbolInfo = class(TJclTD32DataSymbolInfo);
+
+  TJclTD32WithSymbolInfo = class(TJclTD32SymbolInfo)
+  private
+    FOffset: DWORD;
+    FSize: DWORD;
+    FNameIndex: DWORD;
+  public
+    constructor Create(pSymInfo: PSymbolInfo); override;
+    property NameIndex: DWORD read FNameIndex;
+    property Offset: DWORD read FOffset;
+    property Size: DWORD read FSize;
+  end;
+
+  { not used by Delphi }
+  TJclTD32LabelSymbolInfo = class(TJclTD32SymbolInfo)
+  private
+    FOffset: DWORD;
+    FNameIndex: DWORD;
+  public
+    constructor Create(pSymInfo: PSymbolInfo); override;
+    property NameIndex: DWORD read FNameIndex;
+    property Offset: DWORD read FOffset;
+  end;
+
+  { not used by Delphi }
+  TJclTD32ConstantSymbolInfo = class(TJclTD32SymbolInfo)
+  private
+    FValue: DWORD;
+    FTypeIndex: DWORD;
+    FNameIndex: DWORD;
+  public
+    constructor Create(pSymInfo: PSymbolInfo); override;
+    property NameIndex: DWORD read FNameIndex;
+    property TypeIndex: DWORD read FTypeIndex; // for enums
+    property Value: DWORD read FValue;
+  end;
+
+  TJclTD32UdtSymbolInfo = class(TJclTD32SymbolInfo)
+  private
+    FTypeIndex: DWORD;
+    FNameIndex: DWORD;
+    FProperties: Word;
+  public
+    constructor Create(pSymInfo: PSymbolInfo); override;
+    property NameIndex: DWORD read FNameIndex;
+    property TypeIndex: DWORD read FTypeIndex;
+    property Properties: Word read FProperties;
+  end;
+
+  { not used by Delphi }
+  TJclTD32VftPathSymbolInfo = class(TJclTD32SymbolInfo)
+  private
+    FRootIndex: DWORD;
+    FPathIndex: DWORD;
+    FOffset: DWORD;
+  public
+    constructor Create(pSymInfo: PSymbolInfo); override;
+    property RootIndex: DWORD read FRootIndex;
+    property PathIndex: DWORD read FPathIndex;
+    property Offset: DWORD read FOffset;
+  end;
+
+  // TD32 parser
+  TJclTD32InfoParser = class(TObject)
+  private
+    FBase: Pointer;
+    FData: TCustomMemoryStream;
+    FNames: TList;
+    FModules: TObjectList;
+    FSourceModules: TObjectList;
+    FSymbols: TObjectList;
+    FProcSymbols: TList;
+    FValidData: Boolean;
+    FUnmangledNames: TStrings;
+    function GetName(const Idx: Integer): string;
+    function GetNameCount: Integer;
+    function GetSymbol(const Idx: Integer): TJclTD32SymbolInfo;
+    function GetSymbolCount: Integer;
+    function GetProcSymbol(const Idx: Integer): TJclTD32ProcSymbolInfo;
+    function GetProcSymbolCount: Integer;
+    function GetModule(const Idx: Integer): TJclTD32ModuleInfo;
+    function GetModuleCount: Integer;
+    function GetSourceModule(const Idx: Integer): TJclTD32SourceModuleInfo;
+    function GetSourceModuleCount: Integer;
+    function FormatProcName(const ProcName: string): string;
+  protected
+    procedure Analyse;
+    procedure AnalyseNames(const pSubsection: Pointer; const Size: DWORD); virtual;
+    procedure AnalyseGlobalTypes(const pTypes: Pointer; const Size: DWORD); virtual;
+    procedure AnalyseAlignSymbols(pSymbols: PSymbolInfos; const Size: DWORD); virtual;
+    procedure AnalyseModules(pModInfo: PModuleInfo; const Size: DWORD); virtual;
+    procedure AnalyseSourceModules(pSrcModInfo: PSourceModuleInfo; const Size: DWORD); virtual;
+    procedure AnalyseUnknownSubSection(const pSubsection: Pointer; const Size: DWORD); virtual;
+    function LfaToVa(Lfa: DWORD): Pointer;
+  public
+    constructor Create(const ATD32Data: TCustomMemoryStream); // Data mustn't be freed before the class is destroyed
+    destructor Destroy; override;
+    function FindModule(const AAddr: DWORD; out AMod: TJclTD32ModuleInfo): Boolean;
+    function FindSourceModule(const AAddr: DWORD; out ASrcMod: TJclTD32SourceModuleInfo): Boolean;
+    function FindProc(const AAddr: DWORD; out AProc: TJclTD32ProcSymbolInfo): Boolean;
+    procedure GenerateUnmangledNames;
+    class function IsTD32Sign(const Sign: TJclTD32FileSignature): Boolean;
+    class function IsTD32DebugInfoValid(const DebugData: Pointer; const DebugDataSize: LongWord): Boolean;
+    property Data: TCustomMemoryStream read FData;
+    property Names[const Idx: Integer]: string read GetName;
+    property NameCount: Integer read GetNameCount;
+    property Symbols[const Idx: Integer]: TJclTD32SymbolInfo read GetSymbol;
+    property SymbolCount: Integer read GetSymbolCount;
+    property ProcSymbols[const Idx: Integer]: TJclTD32ProcSymbolInfo read GetProcSymbol;
+    property ProcSymbolCount: Integer read GetProcSymbolCount;
+    property Modules[const Idx: Integer]: TJclTD32ModuleInfo read GetModule;
+    property ModuleCount: Integer read GetModuleCount;
+    property SourceModules[const Idx: Integer]: TJclTD32SourceModuleInfo read GetSourceModule;
+    property SourceModuleCount: Integer read GetSourceModuleCount;
+    property ValidData: Boolean read FValidData;
+  end;
+
+  // TD32 scanner with source location methods
+  TJclTD32InfoScanner = class(TJclTD32InfoParser)
+  public
+    function LineNumberFromAddr(AAddr: DWORD; out Offset: Integer): Integer; overload;
+    function LineNumberFromAddr(AAddr: DWORD): Integer; overload;
+    function ProcNameFromAddr(AAddr: DWORD): string; overload;
+    function ProcNameFromAddr(AAddr: DWORD; out Offset: Integer): string; overload;
+    function ModuleNameFromAddr(AAddr: DWORD): string;
+    function SourceNameFromAddr(AAddr: DWORD): string;
+    function VAFromUnitAndProcName(const UnitName, ProcName: string): DWORD;
+  end;
+
+  {$IFDEF BORLAND}
+  // PE Image with TD32 information and source location support 
+  TJclPeBorTD32Image = class(TJclPeBorImage)
+  private
+    FIsTD32DebugPresent: Boolean;
+    FTD32DebugData: TCustomMemoryStream;
+    FTD32Scanner: TJclTD32InfoScanner;
+  protected
+    procedure AfterOpen; override;
+    procedure Clear; override;
+    procedure ClearDebugData;
+    procedure CheckDebugData;
+    function IsDebugInfoInImage(var DataStream: TCustomMemoryStream): Boolean;
+    function IsDebugInfoInTds(var DataStream: TCustomMemoryStream): Boolean;
+  public
+    property IsTD32DebugPresent: Boolean read FIsTD32DebugPresent;
+    property TD32DebugData: TCustomMemoryStream read FTD32DebugData;
+    property TD32Scanner: TJclTD32InfoScanner read FTD32Scanner;
+  end;
+  {$ENDIF BORLAND}
+
+{$IFDEF UNITVERSIONING}
+const
+  UnitVersioning: TUnitVersionInfo = (
+    RCSfile: '$URL$';
+    Revision: '$Revision$';
+    Date: '$Date$';
+    LogPath: 'JCL\source\windows';
+    Extra: '';
+    Data: nil
+    );
+{$ENDIF UNITVERSIONING}
+
+implementation
+
+uses
+  JclResources, JclSysUtils, JclStringConversions;
+
+{$IFDEF BORLAND}
+const
+  TurboDebuggerSymbolExt = '.tds';
+{$ENDIF BORLAND}
+
+//=== { TJclModuleInfo } =====================================================
+
+constructor TJclTD32ModuleInfo.Create(pModInfo: PModuleInfo);
+begin
+  Assert(Assigned(pModInfo));
+  inherited Create;
+  FNameIndex := pModInfo.NameIndex;
+  FSegments := @pModInfo.Segments[0];
+  FSegmentCount := pModInfo.SegmentCount;
+end;
+
+function TJclTD32ModuleInfo.GetSegment(const Idx: Integer): TSegmentInfo;
+begin
+  Assert((0 <= Idx) and (Idx < FSegmentCount));
+  Result := FSegments[Idx];
+end;
+
+//=== { TJclLineInfo } =======================================================
+
+constructor TJclTD32LineInfo.Create(ALineNo, AOffset: DWORD);
+begin
+  inherited Create;
+  FLineNo := ALineNo;
+  FOffset := AOffset;
+end;
+
+//=== { TJclSourceModuleInfo } ===============================================
+
+constructor TJclTD32SourceModuleInfo.Create(pSrcFile: PSourceFileEntry; Base: TJclAddr);
+type
+  PArrayOfWord = ^TArrayOfWord;
+  TArrayOfWord = array [0..MaxInt div SizeOf(Word) - 1] of Word;
+var
+  I, J: Integer;
+  pLineEntry: PLineMappingEntry;
+begin
+  Assert(Assigned(pSrcFile));
+  inherited Create;
+  FNameIndex := pSrcFile.NameIndex;
+  FLines := TObjectList.Create;
+  {$RANGECHECKS OFF}
+  for I := 0 to pSrcFile.SegmentCount - 1 do
+  begin
+    pLineEntry := PLineMappingEntry(Base + pSrcFile.BaseSrcLines[I]);
+    for J := 0 to pLineEntry.PairCount - 1 do
+      FLines.Add(TJclTD32LineInfo.Create(
+        PArrayOfWord(@pLineEntry.Offsets[pLineEntry.PairCount])^[J],
+        pLineEntry.Offsets[J]));
+  end;
+
+  FSegments := @pSrcFile.BaseSrcLines[pSrcFile.SegmentCount];
+  FSegmentCount := pSrcFile.SegmentCount;
+  {$IFDEF RANGECHECKS_ON}
+  {$RANGECHECKS ON}
+  {$ENDIF RANGECHECKS_ON}
+end;
+
+destructor TJclTD32SourceModuleInfo.Destroy;
+begin
+  FreeAndNil(FLines);
+  inherited Destroy;
+end;
+
+function TJclTD32SourceModuleInfo.GetLine(const Idx: Integer): TJclTD32LineInfo;
+begin
+  Result := TJclTD32LineInfo(FLines.Items[Idx]);
+end;
+
+function TJclTD32SourceModuleInfo.GetLineCount: Integer;
+begin
+  Result := FLines.Count;
+end;
+
+function TJclTD32SourceModuleInfo.GetSegment(const Idx: Integer): TOffsetPair;
+begin
+  Assert((0 <= Idx) and (Idx < FSegmentCount));
+  Result := FSegments[Idx];
+end;
+
+function TJclTD32SourceModuleInfo.FindLine(const AAddr: DWORD; out ALine: TJclTD32LineInfo): Boolean;
+var
+  I: Integer;
+begin
+  for I := 0 to LineCount - 1 do
+    with Line[I] do
+    begin
+      if AAddr = Offset then
+      begin
+        Result := True;
+        ALine := Line[I];
+        Exit;
+      end
+      else
+      if (I > 1) and (Line[I - 1].Offset < AAddr) and (AAddr < Offset) then
+      begin
+        Result := True;
+        ALine := Line[I-1];
+        Exit;
+      end;
+    end;
+  Result := False;
+  ALine := nil;
+end;
+
+//=== { TJclSymbolInfo } =====================================================
+
+constructor TJclTD32SymbolInfo.Create(pSymInfo: PSymbolInfo);
+begin
+  Assert(Assigned(pSymInfo));
+  inherited Create;
+  FSymbolType := pSymInfo.SymbolType;
+end;
+
+//=== { TJclProcSymbolInfo } =================================================
+
+constructor TJclTD32ProcSymbolInfo.Create(pSymInfo: PSymbolInfo);
+begin
+  Assert(Assigned(pSymInfo));
+  inherited Create(pSymInfo);
+  with pSymInfo^ do
+  begin
+    FNameIndex := Proc.NameIndex;
+    FOffset := Proc.Offset;
+    FSize := Proc.Size;
+  end;
+end;
+
+//=== { TJclObjNameSymbolInfo } ==============================================
+
+constructor TJclTD32ObjNameSymbolInfo.Create(pSymInfo: PSymbolInfo);
+begin
+  Assert(Assigned(pSymInfo));
+  inherited Create(pSymInfo);
+  with pSymInfo^ do
+  begin
+    FNameIndex := ObjName.NameIndex;
+    FSignature := ObjName.Signature;
+  end;
+end;
+
+//=== { TJclDataSymbolInfo } =================================================
+
+constructor TJclTD32DataSymbolInfo.Create(pSymInfo: PSymbolInfo);
+begin
+  Assert(Assigned(pSymInfo));
+  inherited Create(pSymInfo);
+  with pSymInfo^ do
+  begin
+    FTypeIndex := Data.TypeIndex;
+    FNameIndex := Data.NameIndex;
+    FOffset := Data.Offset;
+  end;
+end;
+
+//=== { TJclWithSymbolInfo } =================================================
+
+constructor TJclTD32WithSymbolInfo.Create(pSymInfo: PSymbolInfo);
+begin
+  Assert(Assigned(pSymInfo));
+  inherited Create(pSymInfo);
+  with pSymInfo^ do
+  begin
+    FNameIndex := With32.NameIndex;
+    FOffset := With32.Offset;
+    FSize := With32.Size;
+  end;
+end;
+
+//=== { TJclLabelSymbolInfo } ================================================
+
+constructor TJclTD32LabelSymbolInfo.Create(pSymInfo: PSymbolInfo);
+begin
+  Assert(Assigned(pSymInfo));
+  inherited Create(pSymInfo);
+  with pSymInfo^ do
+  begin
+    FNameIndex := Label32.NameIndex;
+    FOffset := Label32.Offset;
+  end;
+end;
+
+//=== { TJclConstantSymbolInfo } =============================================
+
+constructor TJclTD32ConstantSymbolInfo.Create(pSymInfo: PSymbolInfo);
+begin
+  Assert(Assigned(pSymInfo));
+  inherited Create(pSymInfo);
+  with pSymInfo^ do
+  begin
+    FNameIndex := Constant.NameIndex;
+    FTypeIndex := Constant.TypeIndex;
+    FValue := Constant.Value;
+  end;
+end;
+
+//=== { TJclUdtSymbolInfo } ==================================================
+
+constructor TJclTD32UdtSymbolInfo.Create(pSymInfo: PSymbolInfo);
+begin
+  Assert(Assigned(pSymInfo));
+  inherited Create(pSymInfo);
+  with pSymInfo^ do
+  begin
+    FNameIndex := Udt.NameIndex;
+    FTypeIndex := Udt.TypeIndex;
+    FProperties := Udt.Properties;
+  end;
+end;
+
+//=== { TJclVftPathSymbolInfo } ==============================================
+
+constructor TJclTD32VftPathSymbolInfo.Create(pSymInfo: PSymbolInfo);
+begin
+  Assert(Assigned(pSymInfo));
+  inherited Create(pSymInfo);
+  with pSymInfo^ do
+  begin
+    FRootIndex := VftPath.RootIndex;
+    FPathIndex := VftPath.PathIndex;
+    FOffset := VftPath.Offset;
+  end;
+end;
+
+//=== { TJclTD32InfoParser } =================================================
+
+constructor TJclTD32InfoParser.Create(const ATD32Data: TCustomMemoryStream);
+begin
+  Assert(Assigned(ATD32Data));
+  inherited Create;
+  FNames := TList.Create;
+  FModules := TObjectList.Create;
+  FSourceModules := TObjectList.Create;
+  FSymbols := TObjectList.Create;
+  FProcSymbols := TList.Create;
+  FNames.Add(nil);
+  FData := ATD32Data;
+  FBase := FData.Memory;
+  FValidData := IsTD32DebugInfoValid(FBase, FData.Size);
+  FUnmangledNames := TStringList.Create;
+  if FValidData then
+    Analyse;
+end;
+
+destructor TJclTD32InfoParser.Destroy;
+begin
+  FreeAndNil(FProcSymbols);
+  FreeAndNil(FSymbols);
+  FreeAndNil(FSourceModules);
+  FreeAndNil(FModules);
+  FreeAndNil(FNames);
+  FreeAndNil(FUnmangledNames);
+  inherited Destroy;
+end;
+
+procedure TJclTD32InfoParser.Analyse;
+var
+  I: Integer;
+  pDirHeader: PDirectoryHeader;
+  pSubsection: Pointer;
+begin
+  pDirHeader := PDirectoryHeader(LfaToVa(PJclTD32FileSignature(LfaToVa(0)).Offset));
+  while True do
+  begin
+    Assert(pDirHeader.DirEntrySize = SizeOf(TDirectoryEntry));
+    {$RANGECHECKS OFF}
+    for I := 0 to pDirHeader.DirEntryCount - 1 do
+      with pDirHeader.DirEntries[I] do
+      begin
+        pSubsection := LfaToVa(Offset);
+        case SubsectionType of
+          SUBSECTION_TYPE_MODULE:
+            AnalyseModules(pSubsection, Size);
+          SUBSECTION_TYPE_ALIGN_SYMBOLS:
+            AnalyseAlignSymbols(pSubsection, Size);
+          SUBSECTION_TYPE_SOURCE_MODULE:
+            AnalyseSourceModules(pSubsection, Size);
+          SUBSECTION_TYPE_NAMES:
+            AnalyseNames(pSubsection, Size);
+          SUBSECTION_TYPE_GLOBAL_TYPES:
+            AnalyseGlobalTypes(pSubsection, Size);
+        else
+          AnalyseUnknownSubSection(pSubsection, Size);
+        end;
+      end;
+    {$IFDEF RANGECHECKS_ON}
+    {$RANGECHECKS ON}
+    {$ENDIF RANGECHECKS_ON}
+    if pDirHeader.lfoNextDir <> 0 then
+      pDirHeader := PDirectoryHeader(LfaToVa(pDirHeader.lfoNextDir))
+    else
+      Break;
+  end;
+end;
+
+procedure TJclTD32InfoParser.AnalyseNames(const pSubsection: Pointer; const Size: DWORD);
+var
+  I, Count, Len: Integer;
+  pszName: PAnsiChar;
+begin
+  Count := PDWORD(pSubsection)^;
+  pszName := PAnsiChar(TJclAddr(pSubsection) + SizeOf(DWORD));
+  if Count > 0 then
+  begin
+    FNames.Capacity := FNames.Capacity + Count;
+    for I := 0 to Count - 1 do
+    begin
+      // Get the length of the name
+      Len := Ord(pszName^);
+      Inc(pszName);
+      // Get the name
+      FNames.Add(pszName);
+      // first, skip the length of name
+      Inc(pszName, Len);
+      // the length is only correct modulo 256 because it is stored on a single byte,
+      // so we have to iterate until we find the real end of the string
+      while PszName^ <> #0 do
+        Inc(pszName, 256);
+      // then, skip a NULL at the end
+      Inc(pszName, 1);
+    end;
+  end;
+end;
+
+{ // unused
+const
+  // Leaf indices for type records that can be referenced from symbols
+  LF_MODIFIER  = $0001;
+  LF_POINTER   = $0002;
+  LF_ARRAY     = $0003;
+  LF_CLASS     = $0004;
+  LF_STRUCTURE = $0005;
+  LF_UNION     = $0006;
+  LF_ENUM      = $0007;
+  LF_PROCEDURE = $0008;
+  LF_MFUNCTION = $0009;
+  LF_VTSHAPE   = $000a;
+  LF_COBOL0    = $000b;
+  LF_COBOL1    = $000c;
+  LF_BARRAY    = $000d;
+  LF_LABEL     = $000e;
+  LF_NULL      = $000f;
+  LF_NOTTRAN   = $0010;
+  LF_DIMARRAY  = $0011;
+  LF_VFTPATH   = $0012;
+
+  // Leaf indices for type records that can be referenced from other type records
+  LF_SKIP       = $0200;
+  LF_ARGLIST    = $0201;
+  LF_DEFARG     = $0202;
+  LF_LIST       = $0203;
+  LF_FIELDLIST  = $0204;
+  LF_DERIVED    = $0205;
+  LF_BITFIELD   = $0206;
+  LF_METHODLIST = $0207;
+  LF_DIMCONU    = $0208;
+  LF_DIMCONLU   = $0209;
+  LF_DIMVARU    = $020a;
+  LF_DIMVARLU   = $020b;
+  LF_REFSYM     = $020c;
+
+  // Leaf indices for fields of complex lists:
+  LF_BCLASS     = $0400;
+  LF_VBCLASS    = $0401;
+  LF_IVBCLASS   = $0402;
+  LF_ENUMERATE  = $0403;
+  LF_FRIENDFCN  = $0404;
+  LF_INDEX      = $0405;
+  LF_MEMBER     = $0406;
+  LF_STMEMBER   = $0407;
+  LF_METHOD     = $0408;
+  LF_NESTTYPE   = $0409;
+  LF_VFUNCTAB   = $040a;
+  LF_FRIENDCLS  = $040b;
+
+  // Leaf indices for numeric fields of symbols and type records:
+  LF_NUMERIC    = $8000;
+  LF_CHAR       = $8001;
+  LF_SHORT      = $8002;
+  LF_USHORT     = $8003;
+  LF_LONG       = $8004;
+  LF_ULONG      = $8005;
+  LF_REAL32     = $8006;
+  LF_REAL64     = $8007;
+  LF_REAL80     = $8008;
+  LF_REAL128    = $8009;
+  LF_QUADWORD   = $800a;
+  LF_UQUADWORD  = $800b;
+  LF_REAL48     = $800c;
+
+  LF_PAD0  = $f0;
+  LF_PAD1  = $f1;
+  LF_PAD2  = $f2;
+  LF_PAD3  = $f3;
+  LF_PAD4  = $f4;
+  LF_PAD5  = $f5;
+  LF_PAD6  = $f6;
+  LF_PAD7  = $f7;
+  LF_PAD8  = $f8;
+  LF_PAD9  = $f9;
+  LF_PAD10 = $fa;
+  LF_PAD11 = $fb;
+  LF_PAD12 = $fc;
+  LF_PAD13 = $fd;
+  LF_PAD14 = $fe;
+  LF_PAD15 = $ff;
+}
+
+type
+  PSymbolTypeInfo = ^TSymbolTypeInfo;
+  TSymbolTypeInfo = packed record
+    TypeId: DWORD;
+    NameIndex: DWORD;  // 0 if unnamed
+    Size: Word;        //  size in bytes of the object
+    MaxSize: Byte;
+    ParentIndex: DWORD;
+  end;
+
+{ unused
+const
+  TID_VOID   = $00;       // Unknown or no type
+  TID_LSTR   = $01;       // Basic Literal string
+  TID_DSTR   = $02;       // Basic Dynamic string
+  TID_PSTR   = $03;       // Pascal style string
+}
+
+procedure TJclTD32InfoParser.AnalyseGlobalTypes(const pTypes: Pointer; const Size: DWORD);
+var
+  pTyp: PSymbolTypeInfo;
+begin
+  pTyp := PSymbolTypeInfo(pTypes);
+  repeat
+    {case pTyp.TypeId of
+      TID_VOID: ;
+    end;}
+    pTyp := PSymbolTypeInfo(TJclAddr(pTyp) + pTyp.Size + SizeOf(pTyp^));
+  until TJclAddr(pTyp) >= TJclAddr(pTypes) + Size;
+end;
+
+procedure TJclTD32InfoParser.AnalyseAlignSymbols(pSymbols: PSymbolInfos; const Size: DWORD);
+var
+  Offset: TJclAddr;
+  pInfo: PSymbolInfo;
+  Symbol: TJclTD32SymbolInfo;
+begin
+  Offset := TJclAddr(@pSymbols.Symbols[0]) - TJclAddr(pSymbols);
+  while Offset < Size do
+  begin
+    pInfo := PSymbolInfo(TJclAddr(pSymbols) + Offset);
+    case pInfo.SymbolType of
+      SYMBOL_TYPE_LPROC32:
+        begin
+          Symbol := TJclTD32LocalProcSymbolInfo.Create(pInfo);
+          FProcSymbols.Add(Symbol);
+        end;
+      SYMBOL_TYPE_GPROC32:
+        begin
+          Symbol := TJclTD32GlobalProcSymbolInfo.Create(pInfo);
+          FProcSymbols.Add(Symbol);
+        end;
+      SYMBOL_TYPE_OBJNAME:
+        Symbol := TJclTD32ObjNameSymbolInfo.Create(pInfo);
+      SYMBOL_TYPE_LDATA32:
+        Symbol := TJclTD32LDataSymbolInfo.Create(pInfo);
+      SYMBOL_TYPE_GDATA32:
+        Symbol := TJclTD32GDataSymbolInfo.Create(pInfo);
+      SYMBOL_TYPE_PUB32:
+        Symbol := TJclTD32PublicSymbolInfo.Create(pInfo);
+      SYMBOL_TYPE_WITH32:
+        Symbol := TJclTD32WithSymbolInfo.Create(pInfo);
+      SYMBOL_TYPE_LABEL32:
+        Symbol := TJclTD32LabelSymbolInfo.Create(pInfo);
+      SYMBOL_TYPE_CONST:
+        Symbol := TJclTD32ConstantSymbolInfo.Create(pInfo);
+      SYMBOL_TYPE_UDT:
+        Symbol := TJclTD32UdtSymbolInfo.Create(pInfo);
+      SYMBOL_TYPE_VFTPATH32:
+        Symbol := TJclTD32VftPathSymbolInfo.Create(pInfo);
+    else
+      Symbol := nil;
+    end;
+    if Assigned(Symbol) then
+      FSymbols.Add(Symbol);
+    Inc(Offset, pInfo.Size + SizeOf(pInfo.Size));
+  end;
+end;
+
+procedure TJclTD32InfoParser.AnalyseModules(pModInfo: PModuleInfo; const Size: DWORD);
+begin
+  FModules.Add(TJclTD32ModuleInfo.Create(pModInfo));
+end;
+
+procedure TJclTD32InfoParser.AnalyseSourceModules(pSrcModInfo: PSourceModuleInfo; const Size: DWORD);
+var
+  I: Integer;
+  pSrcFile: PSourceFileEntry;
+begin
+  {$RANGECHECKS OFF}
+  for I := 0 to pSrcModInfo.FileCount - 1 do
+  begin
+    pSrcFile := PSourceFileEntry(TJclAddr(pSrcModInfo) + pSrcModInfo.BaseSrcFiles[I]);
+    if pSrcFile.NameIndex > 0 then
+      FSourceModules.Add(TJclTD32SourceModuleInfo.Create(pSrcFile, TJclAddr(pSrcModInfo)));
+  end;
+  {$IFDEF RANGECHECKS_ON}
+  {$RANGECHECKS ON}
+  {$ENDIF RANGECHECKS_ON}
+end;
+
+procedure TJclTD32InfoParser.AnalyseUnknownSubSection(const pSubsection: Pointer; const Size: DWORD);
+begin
+  // do nothing
+end;
+
+function TJclTD32InfoParser.FormatProcName(const ProcName: string): string;
+var
+  SecondAtChar, P: PChar;
+begin
+  Result := ProcName;
+  if (Length(ProcName) > 1) and (ProcName[1] = '@') then
+  begin
+    SecondAtChar := StrScan(PChar(ProcName) + 1, '@');
+    if SecondAtChar <> nil then
+    begin
+      Inc(SecondAtChar);
+      Result := SecondAtChar;
+      P := PChar(Result);
+      while P^ <> #0 do
+      begin
+        if (SecondAtChar^ = '@') and ((SecondAtChar - 1)^ <> '@') then
+          P^ := '.';
+        Inc(P);
+        Inc(SecondAtChar);
+      end;
+    end;
+  end;
+
+  if PeIsNameMangled(Result) <> umNotMangled then
+    Result := PeBorUnmangleName(Result);
+end;
+
+procedure TJclTD32InfoParser.GenerateUnmangledNames;
+var
+  I: Integer;
+begin
+  if FUnmangledNames.Count <> 0 then
+    Exit;
+//  FUnmangledNames.Capacity := NameCount;
+  for I := 0 to NameCount - 1 do
+    FUnmangledNames.Add(FormatProcName(UTF8ToString(PAnsiChar(FNames.Items[I]))));
+end;
+
+function TJclTD32InfoParser.GetModule(const Idx: Integer): TJclTD32ModuleInfo;
+begin
+  Result := TJclTD32ModuleInfo(FModules.Items[Idx]);
+end;
+
+function TJclTD32InfoParser.GetModuleCount: Integer;
+begin
+  Result := FModules.Count;
+end;
+
+function TJclTD32InfoParser.GetName(const Idx: Integer): string;
+begin
+  if FUnmangledNames.Count > Idx then
+    Result := FUnmangledNames[Idx]
+  else
+    Result := UTF8ToString(PAnsiChar(FNames.Items[Idx]));
+end;
+
+function TJclTD32InfoParser.GetNameCount: Integer;
+begin
+  Result := FNames.Count;
+end;
+
+function TJclTD32InfoParser.GetSourceModule(const Idx: Integer): TJclTD32SourceModuleInfo;
+begin
+  Result := TJclTD32SourceModuleInfo(FSourceModules.Items[Idx]);
+end;
+
+function TJclTD32InfoParser.GetSourceModuleCount: Integer;
+begin
+  Result := FSourceModules.Count;
+end;
+
+function TJclTD32InfoParser.GetSymbol(const Idx: Integer): TJclTD32SymbolInfo;
+begin
+  Result := TJclTD32SymbolInfo(FSymbols.Items[Idx]);
+end;
+
+function TJclTD32InfoParser.GetSymbolCount: Integer;
+begin
+  Result := FSymbols.Count;
+end;
+
+function TJclTD32InfoParser.GetProcSymbol(const Idx: Integer): TJclTD32ProcSymbolInfo;
+begin
+  Result := TJclTD32ProcSymbolInfo(FProcSymbols.Items[Idx]);
+end;
+
+function TJclTD32InfoParser.GetProcSymbolCount: Integer;
+begin
+  Result := FProcSymbols.Count;
+end;
+
+function TJclTD32InfoParser.FindModule(const AAddr: DWORD; out AMod: TJclTD32ModuleInfo): Boolean;
+var
+  I, J: Integer;
+begin
+  if ValidData then
+    for I := 0 to ModuleCount - 1 do
+    with Modules[I] do
+      for J := 0 to SegmentCount - 1 do
+      begin
+        if (FSegments[J].Flags = 1) and (AAddr >= FSegments[J].Offset) and (AAddr - FSegments[J].Offset <= Segment[J].Size) then
+        begin
+          Result := True;
+          AMod := Modules[I];
+          Exit;
+        end;
+      end;
+  Result := False;
+  AMod := nil;
+end;
+
+function TJclTD32InfoParser.FindSourceModule(const AAddr: DWORD; out ASrcMod: TJclTD32SourceModuleInfo): Boolean;
+var
+  I, J: Integer;
+begin
+  if ValidData then
+    for I := 0 to SourceModuleCount - 1 do
+    with SourceModules[I] do
+      for J := 0 to SegmentCount - 1 do
+        with Segment[J] do
+          if (StartOffset <= AAddr) and (AAddr < EndOffset) then
+          begin
+            Result := True;
+            ASrcMod := SourceModules[I];
+            Exit;
+          end;
+  ASrcMod := nil;
+  Result := False;
+end;
+
+function TJclTD32InfoParser.FindProc(const AAddr: DWORD; out AProc: TJclTD32ProcSymbolInfo): Boolean;
+var
+  I: Integer;
+begin
+  if ValidData then
+    for I := 0 to ProcSymbolCount - 1 do
+    begin
+      AProc := ProcSymbols[I];
+      with AProc do
+        if (Offset <= AAddr) and (AAddr < Offset + Size) then
+        begin
+          Result := True;
+          Exit;
+        end;
+    end;
+  AProc := nil;
+  Result := False;
+end;
+
+class function TJclTD32InfoParser.IsTD32DebugInfoValid(
+  const DebugData: Pointer; const DebugDataSize: LongWord): Boolean;
+var
+  Sign: TJclTD32FileSignature;
+  EndOfDebugData: TJclAddr;
+begin
+  Assert(not IsBadReadPtr(DebugData, DebugDataSize));
+  Result := False;
+  EndOfDebugData := TJclAddr(DebugData) + DebugDataSize;
+  if DebugDataSize > SizeOf(Sign) then
+  begin
+    Sign := PJclTD32FileSignature(EndOfDebugData - SizeOf(Sign))^;
+    if IsTD32Sign(Sign) and (Sign.Offset <= DebugDataSize) then
+    begin
+      Sign := PJclTD32FileSignature(EndOfDebugData - Sign.Offset)^;
+      Result := IsTD32Sign(Sign);
+    end;
+  end;
+end;
+
+class function TJclTD32InfoParser.IsTD32Sign(const Sign: TJclTD32FileSignature): Boolean;
+begin
+  Result := (Sign.Signature = Borland32BitSymbolFileSignatureForDelphi) or
+    (Sign.Signature = Borland32BitSymbolFileSignatureForBCB);
+end;
+
+function TJclTD32InfoParser.LfaToVa(Lfa: DWORD): Pointer;
+begin
+  Result := Pointer(TJclAddr(FBase) + Lfa)
+end;
+
+//=== { TJclTD32InfoScanner } ================================================
+
+function TJclTD32InfoScanner.LineNumberFromAddr(AAddr: DWORD): Integer;
+var
+  Dummy: Integer;
+begin
+  Result := LineNumberFromAddr(AAddr, Dummy);
+end;
+
+function TJclTD32InfoScanner.LineNumberFromAddr(AAddr: DWORD; out Offset: Integer): Integer;
+var
+  ASrcMod: TJclTD32SourceModuleInfo;
+  ALine: TJclTD32LineInfo;
+begin
+  if FindSourceModule(AAddr, ASrcMod) and ASrcMod.FindLine(AAddr, ALine) then
+  begin
+    Result := ALine.LineNo;
+    Offset := AAddr - ALine.Offset;
+  end
+  else
+  begin
+    Result := 0;
+    Offset := 0;
+  end;
+end;
+
+function TJclTD32InfoScanner.ModuleNameFromAddr(AAddr: DWORD): string;
+var
+  AMod: TJclTD32ModuleInfo;
+begin
+  if FindModule(AAddr, AMod) then
+    Result := Names[AMod.NameIndex]
+  else
+    Result := '';
+end;
+
+function TJclTD32InfoScanner.ProcNameFromAddr(AAddr: DWORD): string;
+var
+  Dummy: Integer;
+begin
+  Result := ProcNameFromAddr(AAddr, Dummy);
+end;
+
+function TJclTD32InfoScanner.ProcNameFromAddr(AAddr: DWORD; out Offset: Integer): string;
+var
+  AProc: TJclTD32ProcSymbolInfo;
+begin
+  if FindProc(AAddr, AProc) then
+  begin
+    Result := FormatProcName(Names[AProc.NameIndex]);
+    Offset := AAddr - AProc.Offset;
+  end
+  else
+  begin
+    Result := '';
+    Offset := 0;
+  end;
+end;
+
+function TJclTD32InfoScanner.SourceNameFromAddr(AAddr: DWORD): string;
+var
+  ASrcMod: TJclTD32SourceModuleInfo;
+begin
+  if FindSourceModule(AAddr, ASrcMod) then
+    Result := Names[ASrcMod.NameIndex];
+end;
+
+function TJclTD32InfoScanner.VAFromUnitAndProcName(const UnitName, ProcName: string): DWORD;
+var
+  I: Integer;
+  QualifiedName: string;
+begin
+  Result := 0;
+  if (UnitName = '') or (ProcName = '') then
+    Exit;
+  QualifiedName := UnitName + '.' + ProcName;
+
+  for I := 0 to ProcSymbolCount - 1 do
+  begin
+    if CompareText(FormatProcName(Names[ProcSymbols[I].FNameIndex]), QualifiedName) = 0 then
+    begin
+      Result := ProcSymbols[I].FOffset;
+      Break;
+    end;
+  end;
+end;
+
+{$IFDEF BORLAND}
+
+//=== { TJclPeBorTD32Image } =================================================
+
+procedure TJclPeBorTD32Image.AfterOpen;
+begin
+  inherited AfterOpen;
+  CheckDebugData;
+end;
+
+procedure TJclPeBorTD32Image.CheckDebugData;
+begin
+  FIsTD32DebugPresent := IsDebugInfoInImage(FTD32DebugData);
+  if not FIsTD32DebugPresent then
+    FIsTD32DebugPresent := IsDebugInfoInTds(FTD32DebugData);
+  if FIsTD32DebugPresent then
+  begin
+    FTD32Scanner := TJclTD32InfoScanner.Create(FTD32DebugData);
+    if not FTD32Scanner.ValidData then
+    begin
+      ClearDebugData;
+      if not NoExceptions then
+        raise EJclError.CreateResFmt(@RsHasNotTD32Info, [FileName]);
+    end;
+  end;
+end;
+
+procedure TJclPeBorTD32Image.Clear;
+begin
+  ClearDebugData;
+  inherited Clear;
+end;
+
+procedure TJclPeBorTD32Image.ClearDebugData;
+begin
+  FIsTD32DebugPresent := False;
+  FreeAndNil(FTD32Scanner);
+  FreeAndNil(FTD32DebugData);
+end;
+
+function TJclPeBorTD32Image.IsDebugInfoInImage(var DataStream: TCustomMemoryStream): Boolean;
+var
+  DebugDir: TImageDebugDirectory;
+  BugDataStart: Pointer;
+  DebugDataSize: Integer;
+begin
+  Result := False;
+  DataStream := nil;
+  if IsBorlandImage and (DebugList.Count = 1) then
+  begin
+    DebugDir := DebugList[0];
+    if DebugDir._Type = IMAGE_DEBUG_TYPE_UNKNOWN then
+    begin
+      BugDataStart := RvaToVa(DebugDir.AddressOfRawData);
+      DebugDataSize := DebugDir.SizeOfData;
+      Result := TJclTD32InfoParser.IsTD32DebugInfoValid(BugDataStart, DebugDataSize);
+      if Result then
+        DataStream := TJclReferenceMemoryStream.Create(BugDataStart, DebugDataSize);
+    end;
+  end;
+end;
+
+function TJclPeBorTD32Image.IsDebugInfoInTds(var DataStream: TCustomMemoryStream): Boolean;
+var
+  TdsFileName: TFileName;
+  TempStream: TCustomMemoryStream;
+begin
+  Result := False;
+  DataStream := nil;
+  TdsFileName := ChangeFileExt(FileName, TurboDebuggerSymbolExt);
+  if FileExists(TdsFileName) then
+  begin
+    TempStream := TJclFileMappingStream.Create(TdsFileName, fmOpenRead or fmShareDenyNone);
+    try
+      Result := TJclTD32InfoParser.IsTD32DebugInfoValid(TempStream.Memory, TempStream.Size);
+      if Result then
+        DataStream := TempStream
+      else
+        TempStream.Free;
+    except
+      TempStream.Free;
+      raise;
+    end;
+  end;
+end;
+
+{$ENDIF BORLAND}
+{$IFDEF UNITVERSIONING}
+initialization
+  RegisterUnitVersion(HInstance, UnitVersioning);
+
+finalization
+  UnregisterUnitVersion(HInstance);
+{$ENDIF UNITVERSIONING}
+
+end.