home *** CD-ROM | disk | FTP | other *** search
/ Chip 2003 January / Chip_2003-01_cd1.bin / zkuste / delphi / unity / d56 / FNDUTL.ZIP / Utils / cStrings.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  2002-10-29  |  299.8 KB  |  7,789 lines

  1. {$INCLUDE ..\cDefines.inc}
  2. unit cStrings;
  3.  
  4. {                                                                              }
  5. {                             Ansi Strings v3.32                               }
  6. {                                                                              }
  7. {      This unit is copyright ⌐ 1999-2002 by David Butler (david@e.co.za)      }
  8. {                                                                              }
  9. {                  This unit is part of Delphi Fundamentals.                   }
  10. {                   Its original file name is cStrings.pas                     }
  11. {       The latest version is available from the Fundamentals home page        }
  12. {                     http://fundementals.sourceforge.net/                     }
  13. {                                                                              }
  14. {                I invite you to use this unit, free of charge.                }
  15. {        I invite you to distibute this unit, but it must be for free.         }
  16. {             I also invite you to contribute to its development,              }
  17. {             but do not distribute a modified copy of this file.              }
  18. {                                                                              }
  19. {          A forum is available on SourceForge for general discussion          }
  20. {             http://sourceforge.net/forum/forum.php?forum_id=2117             }
  21. {                                                                              }
  22. {                                                                              }
  23. { Revision history:                                                            }
  24. {   1999/10/19  v0.01  Spawned from Maths unit.                                }
  25. {   1999/10/26  v0.02  Documentation.                                          }
  26. {   1999/10/30  v0.03  Added Count, Reverse.                                   }
  27. {                      Implemented the Boyer-Moore-Horspool pattern searching  }
  28. {                      algorithm in assembly.                                  }
  29. {   1999/10/31  v0.04  Coded Match function in assembly.                       }
  30. {                      Added Replace, Count, PadInside.                        }
  31. {   1999/11/06  v1.05  261 lines interface, 772 lines implementation.          }
  32. {                      Added Remove, TrimEllipse.                              }
  33. {   1999/11/09  v1.06  Added Pack functions.                                   }
  34. {   1999/11/17  v1.07  Added Cut to Pad )                                      }
  35. {                      Added PosN, Before, After and Between.                  }
  36. {                      Added CountWords. Added Split.                          }
  37. {   1999/11/22  v1.08  Added Join. Added Pad (I : Integer).                    }
  38. {   1999/11/23  v1.09  Added Translate.                                        }
  39. {   1999/12/02  v1.10  Added NumToRoman.                                       }
  40. {                      Fixed bugs in Replace and Match reported by             }
  41. {                      daiqingbo@netease.com                                   }
  42. {   1999/12/27  v1.11  Added SelfTest procedure.                               }
  43. {                      Bug fixes. Removed flawed NumToRoman.                   }
  44. {   2000/01/04  v1.12  Added InsensitiveCharSet.                               }
  45. {   2000/01/08  v1.13  Added Append.                                           }
  46. {   2000/05/08  v1.14  Cleaned up unit.                                        }
  47. {   2000/07/20  v1.15  Fixed bug in Match where Position < 0.                  }
  48. {   2000/08/30  v1.16  Fixed bug in Match when S = ''.                         }
  49. {   2000/09/04  v1.17  Added MatchFileMask.                                    }
  50. {   2000/09/31  v1.18  Added HexEscapeText and HexUnescapeText.                }
  51. {   2000/12/04  v1.19  Changes to CopyRange, CopyLeft to avoid memory          }
  52. {                      allocation in specific cases.                           }
  53. {   2001/04/22  v1.20  Added CaseSensitive parameter to Match, PosNext, PosN   }
  54. {   2001/04/25  v1.21  Added CopyEx functions.                                 }
  55. {                      Added MatchLeft/MatchRight.                             }
  56. {                      Updated test cases.                                     }
  57. {   2001/04/26  v1.22  Major refactoring.                                      }
  58. {  -2001/04/28         Replaced PosNext and PosPrev with Pos.                  }
  59. {                      Most functions have compatible parameters now.          }
  60. {                      Added FindFirst, FindFirstUnmatchedRange,               }
  61. {                      IterateMatches.                                         }
  62. {                      1000 lines interface. 3727 lines implementation.        }
  63. {   2001/04/29  v1.23  Added some assembly implementations by Andrew N.        }
  64. {                      Driazgov <andrey@asp.tstu.ru>                           }
  65. {   2001/05/13  v1.24  Added simple regular expression matching.               }
  66. {                      Added CharClassStr conversion routines.                 }
  67. {                      Added PosNext that uses Pos.                            }
  68. {                      1149 lines interface. 4851 lines implementation.        }
  69. {   2001/06/01  v1.25  Added TQuickLexer                                       }
  70. {   2001/07/07  v1.26  Optimizations.                                          }
  71. {   2001/07/30  v1.27  Changed Iterators from objects to records.              }
  72. {   2001/08/22  v1.28  Added LZ-Huffman packer / unpacker.                     }
  73. {                      1429 lines interface. 6445 lines implementation.        }
  74. {   2001/11/11  v2.29  Revision.                                               }
  75. {   2002/02/14  v2.30  Added MatchPattern.                                     }
  76. {   2002/04/03  v3.31  Added string functions from cUtils.                     }
  77. {   2002/04/14  v3.32  Moved TQuickLexer to cQuickLexer.                       }
  78. {                                                                              }
  79. interface
  80.  
  81. uses
  82.   // Delphi
  83.   SysUtils,
  84.  
  85.   // Fundamentals
  86.   cUtils;
  87.  
  88. const
  89.   UnitName      = 'cStrings';
  90.   UnitVersion   = '3.32';
  91.   UnitDesc      = 'Ansi String functions';
  92.   UnitCopyright = '(c) 1999-2002 by David Butler';
  93.  
  94.  
  95.  
  96. {                                                                              }
  97. { Character constants                                                          }
  98. {                                                                              }
  99. const
  100.   // ASCII codes
  101.   ASCII_NULL       = #0;
  102.   ASCII_SOH        = #1;
  103.   ASCII_STX        = #2;
  104.   ASCII_ETX        = #3;
  105.   ASCII_EOT        = #4;
  106.   ASCII_ENQ        = #5;
  107.   ASCII_ACK        = #6;
  108.   ASCII_BEL        = #7;
  109.   ASCII_BS         = #8;
  110.   ASCII_HT         = #9;
  111.   ASCII_LF         = #10;
  112.   ASCII_VT         = #11;
  113.   ASCII_FF         = #12;
  114.   ASCII_CR         = #13;
  115.   ASCII_NAK        = #21;
  116.   ASCII_SYN        = #22;
  117.   ASCII_CAN        = #24;
  118.   ASCII_EOF        = #26;
  119.   ASCII_ESC        = #27;
  120.   ASCII_SP         = #32;
  121.   ASCII_DEL        = #127;
  122.   ASCII_CTL        = [#0..#31];
  123.   ASCII_TEXT       = [#32..#127];
  124.  
  125.   c_Tab            = ASCII_HT;
  126.   c_Space          = ASCII_SP;
  127.   c_DecimalPoint   = '.';
  128.   c_Comma          = ',';
  129.   c_BackSlash      = '\';
  130.   c_ForwardSlash   = '/';
  131.   c_Plus           = '+';
  132.   c_Minus          = '-';
  133.  
  134.   CRLF             = ASCII_CR + ASCII_LF;
  135.  
  136.   cs_AllChars        = [#0..#255];
  137.   cs_ASCII           = ASCII_TEXT;
  138.   cs_NotASCII        = cs_AllChars - cs_ASCII;
  139.   cs_AlphaLow        = ['a'..'z'];
  140.   cs_AlphaUp         = ['A'..'Z'];
  141.   cs_Numeric         = ['0'..'9'];
  142.   cs_NotNumeric      = cs_AllChars - cs_Numeric;
  143.   cs_Alpha           = cs_AlphaLow + cs_AlphaUp;
  144.   cs_NotAlpha        = cs_AllChars - cs_Alpha;
  145.   cs_AlphaNumeric    = cs_Numeric + cs_Alpha;
  146.   cs_NotAlphaNumeric = cs_AllChars - cs_AlphaNumeric;
  147.   cs_WhiteSpace      = ASCII_CTL + [ASCII_SP];
  148.   cs_Exponent        = ['E', 'e'];
  149.   cs_HexDigit        = cs_Numeric + ['A'..'F', 'a'..'f'];
  150.   cs_OctalDigit      = ['0'..'7'];
  151.   cs_BinaryDigit     = ['0'..'1'];
  152.   cs_Sign            = [c_Plus, c_Minus];
  153.   cs_Quotes          = ['"', '''', '`'];
  154.   cs_Parentheses     = ['(', ')'];
  155.   cs_CurlyBrackets   = ['{', '}'];
  156.   cs_BlockBrackets   = ['[', ']'];
  157.   cs_Punctuation     = ['.', ',', ':', '/', '?', '<', '>', ';', '"', '''',
  158.                         '[', ']', '{', '}', '+', '=', '-', '\', '(', ')', '*',
  159.                         '&', '^', '%', '$', '#', '@', '!', '`', '~'];
  160.  
  161.  
  162.  
  163. {                                                                              }
  164. { Type conversion                                                              }
  165. {                                                                              }
  166. Function  StrToFloatDef (const S : String; const Default : Extended) : Extended;
  167. Function  BooleanToStr (const B : Boolean) : String;
  168. Function  StrToBoolean (const S : String) : Boolean;
  169. Function  TVarRecToString (const V : TVarRec; const QuoteStrings : Boolean) : String;
  170.  
  171.  
  172.  
  173. {                                                                              }
  174. { Case conversion                                                              }
  175. {                                                                              }
  176. {   FirstUp returns S with the first letter changed to upper-case.             }
  177. {                                                                              }
  178. Function  LowCase (Ch : Char) : Char;
  179.  
  180. Procedure ConvertUpper (var S : String); overload;
  181. Procedure ConvertLower (var S : String); overload;
  182. Procedure ConvertFirstUp (var S : String);
  183. Function  FirstUp (const S : String) : String;
  184.  
  185. Procedure ConvertUpper (var S : StringArray); overload;
  186. Procedure ConvertLower (var S : StringArray); overload;
  187.  
  188.  
  189.  
  190. {                                                                              }
  191. { Character class strings                                                      }
  192. {                                                                              }
  193. {   Perl-like character class strings, eg the set ['0', 'A'..'Z'] is presented }
  194. {   as '[0A-Z]'. Negated classes also supported, eg '[^A-Za-z]' is all         }
  195. {   non-alpha characters. The empty and complete sets have special             }
  196. {   representations; '[]' and '.' respectively.                                }
  197. {                                                                              }
  198. Function  CharSetToCharClassStr (const C : CharSet) : String;
  199. Function  CharClassStrToCharSet (const S : String) : CharSet;
  200.  
  201.  
  202.  
  203.  
  204. {                                                                              }
  205. { Duplicate                                                                    }
  206. {                                                                              }
  207. Function  Dup (const S : String; const Count : Integer) : String; overload;
  208. Function  Dup (const Ch : Char; const Count : Integer) : String; overload;
  209.  
  210. Function  DupBuf (const Buf; const BufSize : Integer; const Count : Integer) : String; overload;
  211. Function  DupBuf (const Buf; const BufSize : Integer) : String; overload;
  212.  
  213.  
  214.  
  215. {                                                                              }
  216. { Index-based Copy                                                             }
  217. {                                                                              }
  218. {   Variantions on Delphi's Copy. Like Delphi's Copy, invalid values for       }
  219. {   StartIndex (<1,>len), StopIndex (<start,>len) and Count (<0,>end) are      }
  220. {   tolerated (clipped), in other words indexes <1 are treated as 1,           }
  221. {   indexes >len are treated as len and Count past end of string is            }
  222. {   treated as up to end.                                                      }
  223. {   Unlike Delphi's Copy, these versions do not return new strings when        }
  224. {   a reference to an existing string exists.                                  }
  225. {                                                                              }
  226. Function  CopyRange (const S : String; const StartIndex, StopIndex : Integer) : String; overload;
  227. Function  CopyFrom (const S : String; const StartIndex : Integer) : String; overload;
  228. Function  CopyLeft (const S : String; const Count : Integer) : String; overload;
  229. Function  CopyRight (const S : String; const Count : Integer = 1) : String; overload;
  230.  
  231.  
  232.  
  233. {                                                                              }
  234. { Match                                                                        }
  235. {                                                                              }
  236. {   Returns True if M matches at S [StartIndexPos].                            }
  237. {   If StartIndex is invalid, returns False.                                   }
  238. {   For Match with Count parameter, returns True if M matches Count times,     }
  239. {   also returns True if Count <= 0.                                           }
  240. {                                                                              }
  241. Function  MatchNoCase (const A, B : Char) : Boolean;
  242. Function  Match (const A, B : Char; const CaseSensitive : Boolean = True) : Boolean; overload;
  243. Function  Match (const A : CharSet; const B : Char; const CaseSensitive : Boolean = True) : Boolean; overload;
  244. Function  MatchCount (const M : Char; const S : String; const StartIndex : Integer = 1;
  245.           const MaxCount : Integer = -1; const CaseSensitive : Boolean = True) : Integer; overload;
  246. Function  MatchCount (const M : CharSet; const S : String; const StartIndex : Integer = 1;
  247.           const MaxCount : Integer = -1; const CaseSensitive : Boolean = True) : Integer; overload;
  248.  
  249. Function  Match (const M, S : String; const StartIndex : Integer = 1;
  250.           const CaseSensitive : Boolean = True) : Boolean; overload;
  251. Function  MatchBuf (const M : String; const Buf; const BufSize : Integer;
  252.           const CaseSensitive : Boolean = True) : Boolean;
  253. Function  Match (const M : Char; const S : String; const StartIndex : Integer = 1;
  254.           const Count : Integer = 1; const CaseSensitive : Boolean = True) : Boolean; overload;
  255. Function  Match (const M : CharSet; const S : String; const StartIndex : Integer = 1;
  256.           const Count : Integer = 1; const CaseSensitive : Boolean = True) : Boolean; overload;
  257. Function  MatchSeq (const M : Array of CharSet; const S : String; const StartIndex : Integer = 1;
  258.           const CaseSensitive : Boolean = True) : Boolean;
  259.  
  260. Function  MatchChars (const M : Char; const S : Array of Char; const CaseSensitive : Boolean = True) : Integer;
  261. Function  MatchStrings (const M : String; const S : Array of String;
  262.           const CaseSensitive : Boolean = True; const StartIndex : Integer = 1; const MaxMatchLength : Integer = -1) : Integer; overload;
  263. Function  MatchStrings (const M : Array of String; const S : Array of String; var MatchedItem : Integer;
  264.           const CaseSensitive : Boolean = True; const MaxMatchLength : Integer = -1) : Integer; overload;
  265.  
  266. Function  MatchLeft (const M, S : String; const CaseSensitive : Boolean = True) : Boolean;
  267. Function  MatchRight (const M, S : String; const CaseSensitive : Boolean = True) : Boolean;
  268. Function  IsEqualNoCase (const A, B : String) : Boolean;
  269. Function  IsEqual (const A, B : String; const CaseSensitive : Boolean = True) : Boolean; overload;
  270.  
  271.  
  272.  
  273. {                                                                              }
  274. { Fast abbreviated regular expression matcher                                  }
  275. {                                                                              }
  276. {   Matches regular expressions of the form: (<charset><quant>)*               }
  277. {     where <charset> is a character set and <quant> is one of the quantifiers }
  278. {     (mnOnce, mnOptional = ?, mnAny = *, mnLeastOnce = +).                    }
  279. {   Supports deterministic/non-deterministic, greedy/non-greedy matching.      }
  280. {   Returns first MatchPos (as opposed to longest).                            }
  281. {   Uses a NFA (Non-deterministic Finite Automata).                            }
  282. {                                                                              }
  283. {   For example:                                                               }
  284. {     I := 1                                                                   }
  285. {     S := 'a123'                                                              }
  286. {     MatchQuantSeq (I, [['a'..'z'], ['0'..9']], [mqOnce, mqAny], S) = True    }
  287. {                                                                              }
  288. {     is the same as matching the regular expression [a-z][0-9]*               }
  289. {                                                                              }
  290. type
  291.   TMatchQuantifier = (mqOnce, mqAny, mqLeastOnce, mqOptional);
  292.   TMatchQuantSeqOptions = Set of (moDeterministic, moNonGreedy);
  293.  
  294. Function  MatchQuantSeq (var MatchPos : Integer;
  295.           const MatchSeq : Array of CharSet; const Quant : Array of TMatchQuantifier;
  296.           const S : String; const MatchOptions : TMatchQuantSeqOptions = [];
  297.           const StartIndex : Integer = 1; const StopIndex : Integer = -1) : Boolean; overload;
  298.  
  299. type
  300.   TQuantSeq = class
  301.     Sequence: CharSetArray;
  302.     Quantity: Array of TMatchQuantifier;
  303.     Options : TMatchQuantSeqOptions;
  304.  
  305.     Constructor Create (const MatchSeq : Array of CharSet; const Quant : Array of TMatchQuantifier;
  306.                 const MatchOptions : TMatchQuantSeqOptions = []); overload;
  307.  
  308.     Procedure AddToSequence (const Ch : CharSet; const Quant : TMatchQuantifier);
  309.     Procedure AddStringToSequence (const S : String; const CaseSensitive : Boolean = True);
  310.  
  311.     Function  Match (var MatchPos : Integer; const S : String; const StartIndex : Integer = 1;
  312.               const StopIndex : Integer = -1) : Boolean;
  313.   end;
  314.  
  315.  
  316.  
  317. {                                                                              }
  318. { Fast Pattern Matcher                                                         }
  319. {                                                                              }
  320. {   Matches a subset of regular expressions (* ? and [])                       }
  321. {   Matching is non-determistic (ie does backtracking) / non-greedy (ie lazy)  }
  322. {       '*' Matches zero or more of any character                              }
  323. {       '?' Matches exactly one character                                      }
  324. {       [<char set>] Matches character from <char set>                         }
  325. {       [^<char set>] Matches character not in <char set>                      }
  326. {       where <char set> can include multiple ranges and escaped characters    }
  327. {         '\n' matches NewLine (#10), '\r' matches Return (#13)                }
  328. {         '\\' matches a slash ('\'), '\]' matches a close bracket (']'), etc. }
  329. {                                                                              }
  330. {   Examples:                                                                  }
  331. {       MatchPattern ('[a-z0-9_]bc?*c', 'abcabc') = True                       }
  332. {       MatchPattern ('[\\\r\n]+', '\'#13#10) = True                           }
  333. {                                                                              }
  334. Function  MatchPattern (M, S : PChar) : Boolean;
  335.  
  336.  
  337.  
  338. {                                                                              }
  339. { File Mask Matcher                                                            }
  340. {   Matches classic file mask type regular expressions.                        }
  341. {     ? = matches one character (or zero if at end of mask)                    }
  342. {     * = matches zero or more characters                                      }
  343. {                                                                              }
  344. Function  MatchFileMask (const Mask, Key : String; const CaseSensitive : Boolean = False) : Boolean;
  345.  
  346.  
  347.  
  348. {                                                                              }
  349. { Format checking                                                              }
  350. {   Number              [0-9]+                                                 }
  351. {   HexNumber           [0-9A-Fa-f]+                                           }
  352. {   Integer             [+-]? <number>                                         }
  353. {   Real                <integer>? ([.] <number>)?                             }
  354. {   SciReal             <real> ([e] <integer>)?                                }
  355. {   QuotedString        Quote ([^Quote]* ([Quote][Quote])?)* Quote             }
  356. {                                                                              }
  357. {   The Match functions returns the length of the matched text.                }
  358. {                                                                              }
  359. Function  MatchNumber (const S : String; const Index : Integer = 1) : Integer;
  360. Function  MatchHexNumber (const S : String; const Index : Integer = 1) : Integer;
  361. Function  MatchInteger (const S : String; const Index : Integer = 1) : Integer;
  362. Function  MatchReal (const S : String; const Index : Integer = 1) : Integer;
  363. Function  MatchSciReal (const S : String; const Index : Integer = 1) : Integer;
  364. Function  MatchQuotedString (const S : String; const ValidQuotes : CharSet; const Index : Integer = 1) : Integer;
  365.  
  366. Function  IsNumber (const S : String) : Boolean;
  367. Function  IsHexDigit (const C : Char) : Boolean;
  368. Function  HexDigitValue (const C : Char) : Byte;
  369. Function  IsHexNumber (const S : String) : Boolean;
  370. Function  IsInteger (const S : String) : Boolean;
  371. Function  IsReal (const S : String) : Boolean;
  372. Function  IsSciReal (const S : String) : Boolean;
  373. Function  IsQuotedString (const S : String; const ValidQuotes : CharSet = cs_Quotes) : Boolean;
  374.  
  375.  
  376.  
  377. {                                                                              }
  378. { Trim                                                                         }
  379. {   TrimQuotes removes quotes around a string.                                 }
  380. {   TrimEllipse trims the string and puts '...' at the end if it's longer      }
  381. {     than Length.                                                             }
  382. {                                                                              }
  383. Function  TrimLeft (const S : String; const TrimSet : CharSet = cs_WhiteSpace) : String;
  384. Procedure TrimLeftInPlace (var S : String; const TrimSet : CharSet = cs_WhiteSpace);
  385. Function  TrimLeftStr (const S : String; const TrimStr : String;
  386.           const CaseSensitive : Boolean = True) : String;
  387.  
  388. Function  TrimRight (const S : String; const TrimSet : CharSet = cs_WhiteSpace) : String;
  389. Procedure TrimRightInPlace (var S : String; const TrimSet : CharSet = cs_WhiteSpace);
  390. Function  TrimRightStr (const S : String; const TrimStr : String;
  391.           const CaseSensitive : Boolean = True) : String;
  392.  
  393. Function  Trim (const S : String; const TrimSet : CharSet) : String; overload;
  394. Procedure TrimInPlace (var S : String; const TrimSet : CharSet = cs_WhiteSpace);
  395. Function  TrimStr (const S : String; const TrimStr : String;
  396.           const CaseSensitive : Boolean = True) : String; overload;
  397.  
  398. Procedure Trim (var S : StringArray; const TrimSet : CharSet = cs_WhiteSpace); overload;
  399. Procedure TrimStr (var S : StringArray; const TrimStr : String;
  400.           const CaseSensitive : Boolean = True); overload;
  401.  
  402. Function  TrimEllipse (const S : String; const Length : Integer) : String;
  403. Function  TrimQuotes (const S : String; const Quotes : CharSet = cs_Quotes) : String;
  404.  
  405.  
  406.  
  407. {                                                                              }
  408. { Pad                                                                          }
  409. {   The default for Cut is False which won't shorten the string to Length      }
  410. {   if Length < Length (S).                                                    }
  411. {   PadLeft is equivalent to a right justify, PadRight a left justify,         }
  412. {   Pad centering and PadInside a full justification.                          }
  413. {   Pad (I : Integer) left-pad the number with zeros.                          }
  414. {                                                                              }
  415. Function  PadLeft (const S : String; const PadChar : Char; const Length : Integer;
  416.           const Cut : Boolean = False) : String;
  417. Function  PadRight (const S : String; const PadChar : Char; const Length : Integer;
  418.           const Cut : Boolean = False) : String;
  419. Function  Pad (const S : String; const PadChar : Char; const Length : Integer;
  420.           const Cut : Boolean = False) : String; overload;
  421. Function  Pad (const I : Integer; const Length : Integer;
  422.           const Cut : Boolean = False) : String; overload;
  423. Function  PadInside (const S : String; const PadChar : Char; const Length : Integer) : String;
  424.  
  425. type
  426.   TPadType = (padNone, padLeftSpace, padLeftZero, padRightSpace);
  427.  
  428. Function  IntToPadStr (const I : Integer; const PadType : TPadType; const Len : Integer) : String;
  429.  
  430.  
  431.  
  432. {                                                                              }
  433. { Paste                                                                        }
  434. {   Paste copies from Source [SourceStart..SourceStop] to Dest [DestIndex].    }
  435. {     SourceStart, SourceStop and DestPos can be negative to refences indexes  }
  436. {     from the back. Dest will not be grown, Source will be clipped to fit.    }
  437. {   Returns the number of characters moved.                                    }
  438. {   DestIndex is increased/decreased based on ReverseDirection and the number  }
  439. {     of characters moved.                                                     }
  440. {                                                                              }
  441. Function  Paste (const Source : String; var Dest : String; var DestIndex : Integer;
  442.           const ReverseDirection : Boolean = False;
  443.           const SourceStart : Integer = 1; const SourceStop : Integer = -1) : Integer; overload;
  444.  
  445.  
  446.  
  447.  
  448. {                                                                              }
  449. { CopyEx                                                                       }
  450. {   CopyEx functions extend Copy so that Start/Stop values can be negative to  }
  451. {   reference indexes from the back, eg. -2 will reference the second last     }
  452. {   character in the string.                                                   }
  453. {   Invalid values for Start, Stop and Count are tolerated (clipped).          }
  454. {                                                                              }
  455. Function  CopyEx (const S : String; const Start, Count : Integer) : String;
  456. Function  CopyRangeEx (const S : String; const Start, Stop : Integer) : String;
  457. Function  CopyFromEx (const S : String; const Start : Integer) : String;
  458.  
  459.  
  460.  
  461. {                                                                              }
  462. { Find options                                                                 }
  463. {   foReverse         - Search backwards from Stop downto Start.               }
  464. {   foOverlapping     - If Find is a sequence (String, CharSetArray or 'Array  }
  465. {                       of CharSet'), also returns overlapping matches         }
  466. {                       (matches in matches)                                   }
  467. {   foCaseInsensitive - Case insensitive matching.                             }
  468. {   foNonMatch        - Find all non-matches.                                  }
  469. {                                                                              }
  470. type
  471.   TFindOption = (foReverse,
  472.                  foOverlapping,
  473.                  foCaseInsensitive,
  474.                  foNonMatch);
  475.   TFindOptions = Set of TFindOption;
  476.  
  477. Function  FindOptions (const Reverse : Boolean; const CaseInsensitive : Boolean = False;
  478.           const Overlapping : Boolean = False; const NonMatch : Boolean = False) : TFindOptions;
  479.  
  480.  
  481.  
  482. {                                                                              }
  483. { Pos                                                                          }
  484. {   Returns first Match of Find in S between (inclusive) Start and Stop.       }
  485. {   Start and Stop can be negative to refence indexes from the back.           }
  486. {   Invalid values for Start and Stop are tolerated (clipped).                 }
  487. {   Returns 0 if not found.                                                    }
  488. {                                                                              }
  489. {   Patterns for iterating all matches:                                        }
  490. {     I := Pos (Find, S)                       I := 0                          }
  491. {     While I > 0 do                           Repeat                          }
  492. {       begin                            OR      I := PosNext (Find, S, [], I) }
  493. {         ...                                    R := I > 0                    }
  494. {         I := PosNext (Find, S, I)            if R then ...                   }
  495. {       end                                    Until not R                     }
  496. {   (Also see IterateMatches and FindFirst/FindNext)                           }
  497. {                                                                              }
  498. {   TBMHSearcher implements the Boyer-Moore-Horspool pattern searching         }
  499. {   algorithm. The function is faster than Pos for multiple searches for the   }
  500. {   same value of Find (longer strings are better).                            }
  501. {                                                                              }
  502. Function  Pos (const Find, S : String;
  503.           const Options : TFindOptions = [];
  504.           const Start : Integer = 1; const Stop : Integer = -1) : Integer; overload;
  505. Function  Pos (const Find : Char; const S : String;
  506.           const Options : TFindOptions = [];
  507.           const Start : Integer = 1; const Stop : Integer = -1) : Integer; overload;
  508. Function  Pos (const Find : CharSet; const S : String;
  509.           const Options : TFindOptions = [];
  510.           const Start : Integer = 1; const Stop : Integer = -1) : Integer; overload;
  511. Function  PosSeq (const Find : Array of CharSet; const S : String;
  512.           const Options : TFindOptions = [];
  513.           const Start : Integer = 1; const Stop : Integer = -1) : Integer; overload;
  514.  
  515. Function  PosBuf (const Find : String; const Buf; const BufSize : Integer;
  516.           const CaseSensitive : Boolean = True) : Integer;
  517.  
  518. Function  PosNext (const Find, S : String; const LastPos : Integer = 0;
  519.           const Options : TFindOptions = [];
  520.           const Start : Integer = 1; const Stop : Integer = -1) : Integer; overload;
  521. Function  PosNext (const Find : Char; const S : String; const LastPos : Integer = 0;
  522.           const Options : TFindOptions = [];
  523.           const Start : Integer = 1; const Stop : Integer = -1) : Integer; overload;
  524. Function  PosNext (const Find : CharSet; const S : String; const LastPos : Integer = 0;
  525.           const Options : TFindOptions = [];
  526.           const Start : Integer = 1; const Stop : Integer = -1) : Integer; overload;
  527. Function  PosNextSeq (const Find : Array of CharSet; const S : String; const LastPos : Integer = 0;
  528.           const Options : TFindOptions = [];
  529.           const Start : Integer = 1; const Stop : Integer = -1) : Integer; overload;
  530.  
  531. Function  PosChars (const Find : Array of Char; const S : String; var FindItem : Integer;
  532.           const Options : TFindOptions = [];
  533.           const Start : Integer = 1; const Stop : Integer = -1) : Integer;
  534. Function  PosStrings (const Find : Array of String; const S : String; var FindItem : Integer;
  535.           const Options : TFindOptions = [];
  536.           const Start : Integer = 1; const Stop : Integer = -1) : Integer;
  537.  
  538. type
  539.   TBMHSearcher = class
  540.     private
  541.     FTable : Array [#0..#255] of Integer;
  542.     FFind  : String;
  543.  
  544.     public
  545.     Constructor Create (const Find : String);
  546.     Function Pos (const S : String; const StartIndex : Integer = 1;
  547.              const StopIndex : Integer = 0) : Integer;
  548.   end;
  549.  
  550. Function  PosBMH (const Find, S : String; const StartIndex : Integer = 1;
  551.           const StopIndex : Integer = 0) : Integer;
  552.  
  553.  
  554.  
  555. {                                                                              }
  556. { FindFirstPos / FindNextPos Iterators                                         }
  557. {   The iterators are implemented as records.                                  }
  558. {                                                                              }
  559. {   Usage:                                                                     }
  560. {       Procedure X;                                                           }
  561. {       var Iterator : TFindStringIterator                                     }
  562. {         Begin                                                                }
  563. {           if FindFirstPos (Iterator, ...) then                               }
  564. {           ...                                                                }
  565. {                                                                              }
  566. type
  567.   TFindIterator = record
  568.     FS          : String;
  569.     FOptions    : TFindOptions;
  570.     FMaxCount   : Integer;
  571.     FStartIndex : Integer;
  572.     FStopIndex  : Integer;
  573.  
  574.     Index       : Integer;
  575.     Count       : Integer;
  576.   end;
  577.   TFindStringIterator = record
  578.     Iter  : TFindIterator;
  579.     FFind : String;
  580.   end;
  581.   PFindStringIterator = ^TFindStringIterator;
  582.   TFindCharIterator = record
  583.     Iter  : TFindIterator;
  584.     FFind : Char;
  585.   end;
  586.   PFindCharIterator = ^TFindCharIterator;
  587.   TFindCharSetIterator = record
  588.     Iter  : TFindIterator;
  589.     FFind : CharSet;
  590.   end;
  591.   PFindCharSetIterator = ^TFindCharSetIterator;
  592.   TFindCharSetArrayIterator = record
  593.     Iter  : TFindIterator;
  594.     FFind : CharSetArray;
  595.   end;
  596.   PFindCharSetArrayIterator = ^TFindCharSetArrayIterator;
  597.   TFindItemIterator = record
  598.     Iter      : TFindIterator;
  599.     ItemIndex : Integer;
  600.   end;
  601.  
  602.  
  603.  
  604. {                                                                              }
  605. { FindFirstPos/FindNextPos                                                     }
  606. {   FindFirst/FindNext returns the index of the match or 0 if no more matches. }
  607. {   Usage pattern:                                                             }
  608. {       I := FindFirstPos (Iterator, Find, S, ...)                             }
  609. {       While I > 0 do                                                         }
  610. {         begin                                                                }
  611. {           ...                                                                }
  612. {           I := FindNextPos (Iterator)                                        }
  613. {         end                                                                  }
  614. {                                                                              }
  615. Function  FindFirstPos (var Iterator : TFindStringIterator;
  616.           const Find, S : String; const Options : TFindOptions = [];
  617.           const Start : Integer = 1; const Stop : Integer = -1;
  618.           const MaxCount : Integer = -1) : Integer; overload;
  619. Function  FindNextPos (var Iterator : TFindStringIterator) : Integer; overload;
  620.  
  621. Function  FindFirstPos (var Iterator : TFindCharIterator;
  622.           const Find : Char; const S : String; const Options : TFindOptions = [];
  623.           const Start : Integer = 1; const Stop : Integer = -1;
  624.           const MaxCount : Integer = -1) : Integer; overload;
  625. Function  FindNextPos (var Iterator : TFindCharIterator) : Integer; overload;
  626.  
  627. Function  FindFirstPos (var Iterator : TFindCharSetIterator;
  628.           const Find : CharSet; const S : String; const Options : TFindOptions = [];
  629.           const Start : Integer = 1; const Stop : Integer = -1;
  630.           const MaxCount : Integer = -1) : Integer; overload;
  631. Function  FindNextPos (var Iterator : TFindCharSetIterator) : Integer; overload;
  632.  
  633. Function  FindFirstPosSeq (var Iterator : TFindCharSetArrayIterator;
  634.           const Find : Array of CharSet; const S : String; const Options : TFindOptions = [];
  635.           const Start : Integer = 1; const Stop : Integer = -1;
  636.           const MaxCount : Integer = -1) : Integer; overload;
  637. Function  FindNextPosSeq (var Iterator : TFindCharSetArrayIterator) : Integer; overload;
  638.  
  639. Function  FindFirstPos (var Iterator : TFindItemIterator;
  640.           const Find : Array of String; const S : String; const Options : TFindOptions = [];
  641.           const Start : Integer = 1; const Stop : Integer = -1;
  642.           const MaxCount : Integer = -1) : Integer; overload;
  643. Function  FindNextPos (var Iterator : TFindItemIterator;
  644.           const Find : Array of String) : Integer; overload;
  645.  
  646. Function  FindFirstPos (var Iterator : TFindItemIterator;
  647.           const Find : Array of Char; const S : String; const Options : TFindOptions = [];
  648.           const Start : Integer = 1; const Stop : Integer = -1;
  649.           const MaxCount : Integer = -1) : Integer; overload;
  650. Function  FindNextPos (var Iterator : TFindItemIterator;
  651.           const Find : Array of Char) : Integer; overload;
  652.  
  653.  
  654.  
  655. {                                                                              }
  656. { FindFirstUnmatchedRange/FindNextUnmatchedRange                               }
  657. {   Iterates through all the ranges (StartIndex..StopIndex) inbetween matches. }
  658. {                                                                              }
  659. Function  FindFirstUnmatchedRange (var Iterator : TFindStringIterator;
  660.           var StartIndex, StopIndex : Integer;
  661.           const Find, S : String; const Options : TFindOptions = [];
  662.           const Start : Integer = 1; const Stop : Integer = -1;
  663.           const MaxCount : Integer = -1) : Boolean; overload;
  664. Function  FindNextUnmatchedRange (var Iterator : TFindStringIterator;
  665.           var StartIndex, StopIndex : Integer) : Boolean; overload;
  666.  
  667. Function  FindFirstUnmatchedRange (var Iterator : TFindCharIterator;
  668.           var StartIndex, StopIndex : Integer;
  669.           const Find : Char; const S : String; const Options : TFindOptions = [];
  670.           const Start : Integer = 1; const Stop : Integer = -1;
  671.           const MaxCount : Integer = -1) : Boolean; overload;
  672. Function  FindNextUnmatchedRange (var Iterator : TFindCharIterator;
  673.           var StartIndex, StopIndex : Integer) : Boolean; overload;
  674.  
  675. Function  FindFirstUnmatchedRange (var Iterator : TFindCharSetIterator;
  676.           var StartIndex, StopIndex : Integer;
  677.           const Find : CharSet; const S : String; const Options : TFindOptions = [];
  678.           const Start : Integer = 1; const Stop : Integer = -1;
  679.           const MaxCount : Integer = -1) : Boolean; overload;
  680. Function  FindNextUnmatchedRange (var Iterator : TFindCharSetIterator;
  681.           var StartIndex, StopIndex : Integer) : Boolean; overload;
  682. Function  FindFirstUnmatchedRangeSeq (var Iterator : TFindCharSetArrayIterator;
  683.           var StartIndex, StopIndex : Integer;
  684.           const Find : Array of CharSet; const S : String; const Options : TFindOptions = [];
  685.           const Start : Integer = 1; const Stop : Integer = -1;
  686.           const MaxCount : Integer = -1) : Boolean; overload;
  687. Function  FindNextUnmatchedRangeSeq (var Iterator : TFindCharSetArrayIterator;
  688.           var StartIndex, StopIndex : Integer) : Boolean; overload;
  689.  
  690. Function  FindFirstUnmatchedRange (var Iterator : TFindItemIterator;
  691.           var StartIndex, StopIndex : Integer;
  692.           const Find : Array of String; const S : String; const Options : TFindOptions = [];
  693.           const Start : Integer = 1; const Stop : Integer = -1;
  694.           const MaxCount : Integer = -1) : Boolean; overload;
  695. Function  FindNextUnmatchedRange (var Iterator : TFindItemIterator;
  696.           var StartIndex, StopIndex : Integer; const Find : Array of String) : Boolean; overload;
  697.  
  698.  
  699.  
  700. {                                                                              }
  701. { IterateMatches                                                               }
  702. {   IterateMatches iterate through all matches, calling VisitProcedure for     }
  703. {   every match. The Data parameter is passed along with every call.           }
  704. {   IterateMatches returns the number of matches iterated.                     }
  705. {   The callback procedure is called with the match Nr, the Index of the       }
  706. {   match, the passed Data paramater and a Continue variable that can be       }
  707. {   cleared to stop the iteration.                                             }
  708. {                                                                              }
  709. type
  710.   TMatchVisitProcedure = Procedure (const Nr, Index : Integer; const Data : Pointer;
  711.                          var Continue : Boolean; const Iterator : TFindIterator);
  712.  
  713. Function  IterateMatches (const VisitProcedure : TMatchVisitProcedure; const Data : Pointer;
  714.           const Find, S : String;
  715.           const Options : TFindOptions = [];
  716.           const Start : Integer = 1; const Stop : Integer = -1;
  717.           const MaxCount : Integer = -1) : Integer; overload;
  718. Function  IterateMatches (const VisitProcedure : TMatchVisitProcedure; const Data : Pointer;
  719.           const Find : Char; const S : String;
  720.           const Options : TFindOptions = [];
  721.           const Start : Integer = 1; const Stop : Integer = -1;
  722.           const MaxCount : Integer = -1) : Integer; overload;
  723. Function  IterateMatches (const VisitProcedure : TMatchVisitProcedure; const Data : Pointer;
  724.           const Find : CharSet; const S : String;
  725.           const Options : TFindOptions = [];
  726.           const Start : Integer = 1; const Stop : Integer = -1;
  727.           const MaxCount : Integer = -1) : Integer; overload;
  728. Function  IterateMatchesSeq (const VisitProcedure : TMatchVisitProcedure; const Data : Pointer;
  729.           const Find : Array of CharSet; const S : String;
  730.           const Options : TFindOptions = [];
  731.           const Start : Integer = 1; const Stop : Integer = -1;
  732.           const MaxCount : Integer = -1) : Integer; overload;
  733. Function  IterateMatches (const VisitProcedure : TMatchVisitProcedure; const Data : Pointer;
  734.           const Find : Array of String; const S : String;
  735.           const Options : TFindOptions = [];
  736.           const Start : Integer = 1; const Stop : Integer = -1;
  737.           const MaxCount : Integer = -1) : Integer; overload;
  738.  
  739.  
  740.  
  741. {                                                                              }
  742. { Count                                                                        }
  743. {   Returns the number of occurances of Find in S.                             }
  744. {   Start and Stop can be negative to refence indexes from the back.           }
  745. {   If MaxCount = -1 there is no upper limit for counting.                     }
  746. {                                                                              }
  747. Function  Count (const Find, S : String;
  748.           const Options : TFindOptions = [];
  749.           const Start : Integer = 1; const Stop : Integer = -1;
  750.           const MaxCount : Integer = -1) : Integer; overload;
  751. Function  Count (const Find : Char; const S : String;
  752.           const Options : TFindOptions = [];
  753.           const Start : Integer = 1; const Stop : Integer = -1;
  754.           const MaxCount : Integer = -1) : Integer; overload;
  755. Function  Count (const Find : CharSet; const S : String;
  756.           const Options : TFindOptions = [];
  757.           const Start : Integer = 1; const Stop : Integer = -1;
  758.           const MaxCount : Integer = -1) : Integer; overload;
  759. Function  CountSeq (const Find : Array of CharSet; const S : String;
  760.           const Options : TFindOptions = [];
  761.           const Start : Integer = 1; const Stop : Integer = -1;
  762.           const MaxCount : Integer = -1) : Integer; overload;
  763. Function  Count (const Find : Array of String; const S : String;
  764.           const Options : TFindOptions = [];
  765.           const Start : Integer = 1; const Stop : Integer = -1;
  766.           const MaxCount : Integer = -1) : Integer; overload;
  767.  
  768.  
  769.  
  770. {                                                                              }
  771. { PosEx                                                                        }
  772. {   Extended Pos function.                                                     }
  773. {   Returns the index of the Count-th occurance of Find in S (0 if not found). }
  774. {   Start and Stop can be negative to refence indexes from the back.           }
  775. {                                                                              }
  776. Function  PosEx (const Find, S : String;
  777.           const Options : TFindOptions = [];
  778.           const Start : Integer = 1; const Stop : Integer = -1;
  779.           const Count : Integer = 1) : Integer; overload;
  780. Function  PosEx (const Find : CharSet; const S : String;
  781.           const Options : TFindOptions = [];
  782.           const Start : Integer = 1; const Stop : Integer = -1;
  783.           const Count : Integer = 1) : Integer; overload;
  784. Function  PosEx (const Find : Char; const S : String;
  785.           const Options : TFindOptions = [];
  786.           const Start : Integer = 1; const Stop : Integer = -1;
  787.           const Count : Integer = 1) : Integer; overload;
  788. Function  PosExSeq (const Find : Array of CharSet; const S : String;
  789.           const Options : TFindOptions = [];
  790.           const Start : Integer = 1; const Stop : Integer = -1;
  791.           const Count : Integer = 1) : Integer; overload;
  792.  
  793.  
  794.  
  795. {                                                                              }
  796. { FindAll                                                                      }
  797. {   Returns an IntegerArray with the indexes of all matched positions.         }
  798. {   Start and Stop can be negative to refence indexes from the back.           }
  799. {   Set MaxCount = -1 for no limit.                                            }
  800. {   If Algorithm = faSingleAllocation then the memory for the result is        }
  801. {     allocated once, by first counting the matches. This can be faster in     }
  802. {     some cases where a lot of matches are returned.                          }
  803. {                                                                              }
  804. type
  805.   TFindAllAlgorithm = (faSingleAllocation, faSingleIteration);
  806.  
  807. Function  FindAll (const Find, S : String;
  808.           const Options : TFindOptions = [];
  809.           const Start : Integer = 1; const Stop : Integer = -1;
  810.           const MaxCount : Integer = -1;
  811.           const Algorithm : TFindAllAlgorithm = faSingleIteration) : IntegerArray; overload;
  812. Function  FindAll (const Find : Char; const S : String;
  813.           const Options : TFindOptions = [];
  814.           const Start : Integer = 1; const Stop : Integer = -1;
  815.           const MaxCount : Integer = -1;
  816.           const Algorithm : TFindAllAlgorithm = faSingleIteration) : IntegerArray; overload;
  817. Function  FindAll (const Find : CharSet; const S : String;
  818.           const Options : TFindOptions = [];
  819.           const Start : Integer = 1; const Stop : Integer = -1;
  820.           const MaxCount : Integer = -1;
  821.           const Algorithm : TFindAllAlgorithm = faSingleIteration) : IntegerArray; overload;
  822. Function  FindAllSeq (const Find : Array of CharSet; const S : String;
  823.           const Options : TFindOptions = [];
  824.           const Start : Integer = 1; const Stop : Integer = -1;
  825.           const MaxCount : Integer = -1;
  826.           const Algorithm : TFindAllAlgorithm = faSingleIteration) : IntegerArray; overload;
  827.  
  828.  
  829.  
  830. {                                                                              }
  831. { Split/Join                                                                   }
  832. {   Function Split splits S into an array on a Delimiter. If Delimiter=''      }
  833. {     or S='' then Split returns an empty array. If Token not found in S,      }
  834. {     it returns an array with one element, S.                                 }
  835. {     Works for foReverse option. foOverlapping option ignored.                }
  836. {   Procedure Split splits S into two parts.                                   }
  837. {     If SplitPosition = splitLeft, the Delimiter is part of LeftSide; for     }
  838. {       splitRight the Delimiter is part of RightSide; and for splitCenter     }
  839. {       the Delimiter is not part of LeftSide nor RightSide.                   }
  840. {     If Delimiter is not found and DelimiterOptional = True then LeftSide = S }
  841. {       else if DelimiterOptional = False then LeftSide = ''.                  }
  842. {     Do not use it as follow:                                                 }
  843. {       Split (S, Delimiter, S, T). Delphi's reference counting gets confused. }
  844. {                                                                              }
  845. type
  846.   TSplitAlgorithm = (saSingleAllocation, saSingleIteration);
  847.  
  848. Function  Split (const S, Delimiter : String;
  849.           const Options : TFindOptions = [];
  850.           const Start : Integer = 1; const Stop : Integer = -1;
  851.           const MaxCount : Integer = -1;
  852.           const Algorithm : TSplitAlgorithm = saSingleIteration) : StringArray; overload;
  853. Function  Split (const S : String; const Delimiter : Char = c_Space;
  854.           const Options : TFindOptions = [];
  855.           const Start : Integer = 1; const Stop : Integer = -1;
  856.           const MaxCount : Integer = -1;
  857.           const Algorithm : TSplitAlgorithm = saSingleIteration) : StringArray; overload;
  858. Function  Split (const S : String; const Delimiter : CharSet = cs_WhiteSpace;
  859.           const Options : TFindOptions = [];
  860.           const Start : Integer = 1; const Stop : Integer = -1;
  861.           const MaxCount : Integer = -1;
  862.           const Algorithm : TSplitAlgorithm = saSingleIteration) : StringArray; overload;
  863. Function  Join (const S : Array of String; const Delimiter : String = c_Space;
  864.           const Start : Integer = 0) : String;
  865.  
  866. type
  867.   TSplitPosition = (splitCenter, splitLeft, splitRight);
  868.  
  869. Procedure Split (const S, Delimiter : String; var LeftSide, RightSide : String;
  870.           const DelimiterOptional : Boolean = True;
  871.           const SplitPosition : TSplitPosition = splitCenter;
  872.           const Options : TFindOptions = [];
  873.           const Start : Integer = 1; const Stop : Integer = -1); overload;
  874. Procedure Split (const S : String; const Delimiter : CharSet; var LeftSide, RightSide : String;
  875.           const DelimiterOptional : Boolean = True;
  876.           const SplitPosition : TSplitPosition = splitCenter;
  877.           const Options : TFindOptions = [];
  878.           const Start : Integer = 1; const Stop : Integer = -1); overload;
  879.  
  880. Function  ExtractWords (const S : String; const WordChars : CharSet) : StringArray;
  881.  
  882.  
  883.  
  884. {                                                                              }
  885. { Cut                                                                          }
  886. {   Cut returns and deletes the specified characters from S.                   }
  887. {                                                                              }
  888. Function  Cut (var S : String; const Index, Count : Integer) : String;
  889. Function  CutLeft (var S : String; const Count : Integer) : String;
  890. Function  CutRight (var S : String; const Count : Integer) : String;
  891.  
  892. Function  CutTo (var S : String; const Delimiter : Char;
  893.           const DelimiterOptional : Boolean = True;
  894.           const FindOptions : TFindOptions = [];
  895.           const Start : Integer = 1; const Stop : Integer = -1;
  896.           const Count : Integer = 1) : String;
  897.  
  898.  
  899.  
  900. {                                                                              }
  901. { Replace                                                                      }
  902. {   Replace returns a string with Find replaced with Replace.                  }
  903. {   If MaxCount = -1 then all occurances off Find is replaced.                 }
  904. {   ReplaceChars uses arrays parameters for Find and Replace.                  }
  905. {     It replaces the Find entries with their associated Replace entries.      }
  906. {     Find and Replace must have an equal number of entries.                   }
  907. {   Remove removes all characters in Ch from S.                                }
  908. {   RemoveDup replaces all duplicate occurances of Ch with a single occurance. }
  909. {                                                                              }
  910. type
  911.   TReplaceAlgorithm = (raSingleAllocation, raSingleIteration);
  912.  
  913. Function  Replace (const Find, Replace, S : String;
  914.           const Options : TFindOptions = [];
  915.           const Start : Integer = 1; const Stop : Integer = -1;
  916.           const MaxCount : Integer = -1;
  917.           const Algorithm : TReplaceAlgorithm = raSingleAllocation) : String; overload;
  918. Function  ReplaceSeq (const Find : Array of CharSet; const Replace, S : String;
  919.           const Options : TFindOptions = [];
  920.           const Start : Integer = 1; const Stop : Integer = -1;
  921.           const MaxCount : Integer = -1;
  922.           const Algorithm : TReplaceAlgorithm = raSingleAllocation) : String; overload;
  923. Function  Replace (const Find : Char; const Replace, S : String;
  924.           const Options : TFindOptions = [];
  925.           const Start : Integer = 1; const Stop : Integer = -1;
  926.           const MaxCount : Integer = -1;
  927.           const Algorithm : TReplaceAlgorithm = raSingleAllocation) : String; overload;
  928. Function  Replace (const Find : CharSet; const Replace, S : String;
  929.           const Options : TFindOptions = [];
  930.           const Start : Integer = 1; const Stop : Integer = -1;
  931.           const MaxCount : Integer = -1;
  932.           const Algorithm : TReplaceAlgorithm = raSingleAllocation) : String; overload;
  933. Function  Replace (const Find, Replace : Char; const S : String;
  934.           const Options : TFindOptions = [];
  935.           const Start : Integer = 1; const Stop : Integer = -1;
  936.           const MaxCount : Integer = -1) : String; overload;
  937. Function  Replace (const Find : CharSet; const Replace : Char; const S : String;
  938.           const Options : TFindOptions = [];
  939.           const Start : Integer = 1; const Stop : Integer = -1;
  940.           const MaxCount : Integer = -1) : String; overload;
  941.  
  942. Function  ReplaceChars (const Find, Replace : Array of Char; const S : String;
  943.           const Options : TFindOptions = [];
  944.           const Start : Integer = 1; const Stop : Integer = -1;
  945.           const MaxCount : Integer = -1) : String;
  946.  
  947. Function  RemoveAll (const Find : Char; const S : String;
  948.           const Options : TFindOptions = [];
  949.           const Start : Integer = 1; const Stop : Integer = -1;
  950.           const Algorithm : TReplaceAlgorithm = raSingleAllocation) : String; overload;
  951. Function  RemoveAll (const Find : CharSet; const S : String;
  952.           const Options : TFindOptions = [];
  953.           const Start : Integer = 1; const Stop : Integer = -1;
  954.           const Algorithm : TReplaceAlgorithm = raSingleAllocation) : String; overload;
  955. Function  RemoveAll (const Find, S : String;
  956.           const Options : TFindOptions = [];
  957.           const Start : Integer = 1; const Stop : Integer = -1;
  958.           const Algorithm : TReplaceAlgorithm = raSingleAllocation) : String; overload;
  959. Function  RemoveFirst (const Find, S : String;
  960.           const Options : TFindOptions = [];
  961.           const Start : Integer = 1; const Stop : Integer = -1;
  962.           const Algorithm : TReplaceAlgorithm = raSingleAllocation) : String; overload;
  963.  
  964. Function  RemoveSeq (const C : Array of CharSet; const S : String) : String;
  965. Function  RemoveDup (const C : Char; const S : String) : String;
  966.  
  967.  
  968.  
  969. {                                                                              }
  970. { Delimiter-based Copy                                                         }
  971. {   Similar to Copy functions, but use Delimiters instead of indexes.          }
  972. {   Returns S [Start..Stop] instead of '' if DelimiterOptional and the         }
  973. {     Delimiter is not found in S.                                             }
  974. {   RemoveBetween := CopyBefore (CopyAfter (S, LeftDelimiter), RightDelimiter) }
  975. {   For Count <= 0 the Delimiter is not located and result is same as          }
  976. {     when doOptional and Delimiter not found.                                 }
  977. {                                                                              }
  978. type
  979.   TDelimiterOption = (doOptional, doIncludeDelimiter);
  980.   TDelimiterOptions = Set of TDelimiterOption;
  981.  
  982. Function  CopyLeft (const S, Delimiter : String;
  983.           const DelimiterOptions : TDelimiterOptions = [doOptional];
  984.           const FindOptions : TFindOptions = [];
  985.           const Start : Integer = 1; const Stop : Integer = -1;
  986.           const Count : Integer = 1) : String; overload;
  987. Function  CopyLeft (const S : String; const Delimiter : CharSet;
  988.           const DelimiterOptions : TDelimiterOptions = [doOptional];
  989.           const FindOptions : TFindOptions = [];
  990.           const Start : Integer = 1; const Stop : Integer = -1;
  991.           const Count : Integer = 1) : String; overload;
  992. Function  CopyRight (const S, Delimiter : String;
  993.           const DelimiterOptions : TDelimiterOptions = [];
  994.           const FindOptions : TFindOptions = [];
  995.           const Start : Integer = 1; const Stop : Integer = -1;
  996.           const Count : Integer = 1) : String; overload;
  997. Function  CopyRight (const S : String; const Delimiter : CharSet;
  998.           const DelimiterOptions : TDelimiterOptions = [];
  999.           const FindOptions : TFindOptions = [];
  1000.           const Start : Integer = 1; const Stop : Integer = -1;
  1001.           const Count : Integer = 1) : String; overload;
  1002.  
  1003. Function  CopyRange (const S, LeftDelimiter, RightDelimiter : String;
  1004.           const LeftDelimiterOptions : TDelimiterOptions = [];
  1005.           const RightDelimiterOptions : TDelimiterOptions = [doOptional];
  1006.           const NotRange : Boolean = False;
  1007.           const LeftFindOptions : TFindOptions = [];
  1008.           const RightFindOptions : TFindOptions = [];
  1009.           const Start : Integer = 1; const Stop : Integer = -1;
  1010.           const LeftCount : Integer = 1; const RightCount : Integer = 1) : String; overload;
  1011. Function  CopyRange (const S, LeftDelimiter : String; const RightDelimiter : CharSet;
  1012.           const LeftDelimiterOptions : TDelimiterOptions = [];
  1013.           const RightDelimiterOptions : TDelimiterOptions = [doOptional];
  1014.           const NotRange : Boolean = False;
  1015.           const LeftFindOptions : TFindOptions = [];
  1016.           const RightFindOptions : TFindOptions = [];
  1017.           const Start : Integer = 1; const Stop : Integer = -1;
  1018.           const LeftCount : Integer = 1; const RightCount : Integer = 1) : String; overload;
  1019. Function  CopyRange (const S : String; const LeftDelimiter : CharSet; const RightDelimiter : String;
  1020.           const LeftDelimiterOptions : TDelimiterOptions = [];
  1021.           const RightDelimiterOptions : TDelimiterOptions = [doOptional];
  1022.           const NotRange : Boolean = False;
  1023.           const LeftFindOptions : TFindOptions = [];
  1024.           const RightFindOptions : TFindOptions = [];
  1025.           const Start : Integer = 1; const Stop : Integer = -1;
  1026.           const LeftCount : Integer = 1; const RightCount : Integer = 1) : String; overload;
  1027. Function  CopyRange (const S : String; const LeftDelimiter, RightDelimiter : CharSet;
  1028.           const LeftDelimiterOptions : TDelimiterOptions = [];
  1029.           const RightDelimiterOptions : TDelimiterOptions = [doOptional];
  1030.           const NotRange : Boolean = False;
  1031.           const LeftFindOptions : TFindOptions = [];
  1032.           const RightFindOptions : TFindOptions = [];
  1033.           const Start : Integer = 1; const Stop : Integer = -1;
  1034.           const LeftCount : Integer = 1; const RightCount : Integer = 1) : String; overload;
  1035.  
  1036. Function  CopyFrom (const S, Delimiter : String;
  1037.           const DelimiterOptional : Boolean = False;
  1038.           const FindOptions : TFindOptions = [];
  1039.           const Start : Integer = 1; const Stop : Integer = -1;
  1040.           const Count : Integer = 1) : String; overload;
  1041. Function  CopyFrom (const S : String; const Delimiter : CharSet;
  1042.           const DelimiterOptional : Boolean = False;
  1043.           const FindOptions : TFindOptions = [];
  1044.           const Start : Integer = 1; const Stop : Integer = -1;
  1045.           const Count : Integer = 1) : String; overload;
  1046. Function  CopyAfter (const S, Delimiter : String;
  1047.           const DelimiterOptional : Boolean = False;
  1048.           const FindOptions : TFindOptions = [];
  1049.           const Start : Integer = 1; const Stop : Integer = -1;
  1050.           const Count : Integer = 1) : String; overload;
  1051. Function  CopyAfter (const S : String; const Delimiter : CharSet;
  1052.           const DelimiterOptional : Boolean = False;
  1053.           const FindOptions : TFindOptions = [];
  1054.           const Start : Integer = 1; const Stop : Integer = -1;
  1055.           const Count : Integer = 1) : String; overload;
  1056. Function  CopyTo (const S, Delimiter : String;
  1057.           const DelimiterOptional : Boolean = True;
  1058.           const FindOptions : TFindOptions = [];
  1059.           const Start : Integer = 1; const Stop : Integer = -1;
  1060.           const Count : Integer = 1) : String; overload;
  1061. Function  CopyTo (const S : String; const Delimiter : CharSet;
  1062.           const DelimiterOptional : Boolean = True;
  1063.           const FindOptions : TFindOptions = [];
  1064.           const Start : Integer = 1; const Stop : Integer = -1;
  1065.           const Count : Integer = 1) : String; overload;
  1066. Function  CopyBefore (const S, Delimiter : String;
  1067.           const DelimiterOptional : Boolean = True;
  1068.           const FindOptions : TFindOptions = [];
  1069.           const Start : Integer = 1; const Stop : Integer = -1;
  1070.           const Count : Integer = 1) : String; overload;
  1071. Function  CopyBefore (const S : String; const Delimiter : CharSet;
  1072.           const DelimiterOptional : Boolean = True;
  1073.           const FindOptions : TFindOptions = [];
  1074.           const Start : Integer = 1; const Stop : Integer = -1;
  1075.           const Count : Integer = 1) : String; overload;
  1076.  
  1077. Function  CopyBetween (const S, LeftDelimiter, RightDelimiter : String;
  1078.           const LeftDelimiterOptional : Boolean = False;
  1079.           const RightDelimiterOptional : Boolean = True;
  1080.           const LeftFindOptions : TFindOptions = [];
  1081.           const RightFindOptions : TFindOptions = [];
  1082.           const Start : Integer = 1; const Stop : Integer = -1;
  1083.           const LeftCount : Integer = 1; const RightCount : Integer = 1) : String; overload;
  1084. Function  CopyBetween (const S, LeftDelimiter : String; const RightDelimiter : CharSet;
  1085.           const LeftDelimiterOptional : Boolean = False;
  1086.           const RightDelimiterOptional : Boolean = True;
  1087.           const LeftFindOptions : TFindOptions = [];
  1088.           const RightFindOptions : TFindOptions = [];
  1089.           const Start : Integer = 1; const Stop : Integer = -1;
  1090.           const LeftCount : Integer = 1; const RightCount : Integer = 1) : String; overload;
  1091. Function  CopyBetween (const S : String; const LeftDelimiter, RightDelimiter : CharSet;
  1092.           const LeftDelimiterOptional : Boolean = False;
  1093.           const RightDelimiterOptional : Boolean = True;
  1094.           const LeftFindOptions : TFindOptions = [];
  1095.           const RightFindOptions : TFindOptions = [];
  1096.           const Start : Integer = 1; const Stop : Integer = -1;
  1097.           const LeftCount : Integer = 1; const RightCount : Integer = 1) : String; overload;
  1098. Function  CopyBetween (const S : String; const LeftDelimiter : CharSet; const RightDelimiter : String;
  1099.           const LeftDelimiterOptional : Boolean = False;
  1100.           const RightDelimiterOptional : Boolean = True;
  1101.           const LeftFindOptions : TFindOptions = [];
  1102.           const RightFindOptions : TFindOptions = [];
  1103.           const Start : Integer = 1; const Stop : Integer = -1;
  1104.           const LeftCount : Integer = 1; const RightCount : Integer = 1) : String; overload;
  1105.  
  1106. Function  RemoveBetween (const S, LeftDelimiter, RightDelimiter : String;
  1107.           const LeftDelimiterOptional : Boolean = False;
  1108.           const RightDelimiterOptional : Boolean = True;
  1109.           const LeftFindOptions : TFindOptions = [];
  1110.           const RightFindOptions : TFindOptions = [];
  1111.           const Start : Integer = 1; const Stop : Integer = -1;
  1112.           const LeftCount : Integer = 1; const RightCount : Integer = 1) : String; overload;
  1113. Function  RemoveBetween (const S, LeftDelimiter : String; const RightDelimiter : CharSet;
  1114.           const LeftDelimiterOptional : Boolean = False;
  1115.           const RightDelimiterOptional : Boolean = True;
  1116.           const LeftFindOptions : TFindOptions = [];
  1117.           const RightFindOptions : TFindOptions = [];
  1118.           const Start : Integer = 1; const Stop : Integer = -1;
  1119.           const LeftCount : Integer = 1; const RightCount : Integer = 1) : String; overload;
  1120. Function  RemoveBetween (const S : String; const LeftDelimiter, RightDelimiter : CharSet;
  1121.           const LeftDelimiterOptional : Boolean = False;
  1122.           const RightDelimiterOptional : Boolean = True;
  1123.           const LeftFindOptions : TFindOptions = [];
  1124.           const RightFindOptions : TFindOptions = [];
  1125.           const Start : Integer = 1; const Stop : Integer = -1;
  1126.           const LeftCount : Integer = 1; const RightCount : Integer = 1) : String; overload;
  1127. Function  RemoveBetween (const S : String; const LeftDelimiter : CharSet; const RightDelimiter : String;
  1128.           const LeftDelimiterOptional : Boolean = False;
  1129.           const RightDelimiterOptional : Boolean = True;
  1130.           const LeftFindOptions : TFindOptions = [];
  1131.           const RightFindOptions : TFindOptions = [];
  1132.           const Start : Integer = 1; const Stop : Integer = -1;
  1133.           const LeftCount : Integer = 1; const RightCount : Integer = 1) : String; overload;
  1134.  
  1135. Function  Remove (const S, LeftDelimiter, RightDelimiter : String;
  1136.           const LeftDelimiterOptional : Boolean = False;
  1137.           const RightDelimiterOptional : Boolean = False;
  1138.           const LeftFindOptions : TFindOptions = [];
  1139.           const RightFindOptions : TFindOptions = [];
  1140.           const Start : Integer = 1; const Stop : Integer = -1;
  1141.           const LeftCount : Integer = 1; const RightCount : Integer = 1) : String; overload;
  1142. Function  Remove (const S, LeftDelimiter : String; const RightDelimiter : CharSet;
  1143.           const LeftDelimiterOptional : Boolean = False;
  1144.           const RightDelimiterOptional : Boolean = False;
  1145.           const LeftFindOptions : TFindOptions = [];
  1146.           const RightFindOptions : TFindOptions = [];
  1147.           const Start : Integer = 1; const Stop : Integer = -1;
  1148.           const LeftCount : Integer = 1; const RightCount : Integer = 1) : String; overload;
  1149. Function  Remove (const S : String; const LeftDelimiter, RightDelimiter : CharSet;
  1150.           const LeftDelimiterOptional : Boolean = False;
  1151.           const RightDelimiterOptional : Boolean = False;
  1152.           const LeftFindOptions : TFindOptions = [];
  1153.           const RightFindOptions : TFindOptions = [];
  1154.           const Start : Integer = 1; const Stop : Integer = -1;
  1155.           const LeftCount : Integer = 1; const RightCount : Integer = 1) : String; overload;
  1156. Function  Remove (const S : String; const LeftDelimiter : CharSet; const RightDelimiter : String;
  1157.           const LeftDelimiterOptional : Boolean = False;
  1158.           const RightDelimiterOptional : Boolean = False;
  1159.           const LeftFindOptions : TFindOptions = [];
  1160.           const RightFindOptions : TFindOptions = [];
  1161.           const Start : Integer = 1; const Stop : Integer = -1;
  1162.           const LeftCount : Integer = 1; const RightCount : Integer = 1) : String; overload;
  1163.  
  1164.  
  1165.  
  1166. {                                                                              }
  1167. { Quoting, Escaping and Translating                                            }
  1168. {                                                                              }
  1169. {   EscapeText/UnescapeText converts text where escaping is done with a        }
  1170. {   single character (EscapePrefix) followed by a single character identifier  }
  1171. {   (EscapeChar).                                                              }
  1172. {   When AlwaysDropPrefix = True, the prefix will be dropped from the          }
  1173. {   resulting string if it is not followed by one of EscapeChar.               }
  1174. {   Examples:                                                                  }
  1175. {     S := EscapeText (S, [#0, #10, #13, '\'], '\', ['0', 'n', 'r', '\']);     }
  1176. {     S := UnescapeText (S, '\', ['0', 'n', 'r', '\'], [#0, #10, #13, '\']);   }
  1177. {     S := EscapeText (S, [''''], '''', ['''']);                               }
  1178. {                                                                              }
  1179. {   QuoteText, UnquoteText converts text where the string is enclosed in a     }
  1180. {   pair of the same quote characters, and two consequetive occurance of the   }
  1181. {   quote character inside the quotes indicate a quote character in the text.  }
  1182. {   Examples:                                                                  }
  1183. {     QuoteText ('abc', '"') = '"abc"'                                         }
  1184. {     QuoteText ('a"b"c', '"') = '"a""b""c"'                                   }
  1185. {     UnquoteText ('"a""b""c"') = 'a"b"c'                                      }
  1186. {                                                                              }
  1187. {   RemoveQuotes simply removes opening and closing quotes around a string.    }
  1188. {                                                                              }
  1189. Function  EscapeText (const S : String; const CharsToEscape : Array of Char;
  1190.           const EscapePrefix : Char; const EscapeChar : Array of Char) : String;
  1191. Function  UnescapeText (const S : String; const EscapePrefix : Char;
  1192.           const EscapeChar : Array of Char; const Replacement : Array of String;
  1193.           const AlwaysDropPrefix : Boolean = False) : String;
  1194. Function  CEscapeText (const S : String) : String;
  1195. Function  CUnescapeText (const S : String) : String;
  1196. Function  QuoteText (const S : String; const Quotes : Char = '''') : String;
  1197. Function  UnquoteText (const S : String) : String;
  1198. Function  RemoveQuotes (const S : String; const Quotes : Char = '''') : String;
  1199. Function  HexEscapeText (const S : String; const CharsToEscape : CharSet;
  1200.           const EscapePrefix : String; const EscapePostfix : String = '';
  1201.           const UpperHex : Boolean = False; const AlwaysTwoDigits : Boolean = True) : String;
  1202. Function  HexUnescapeText (const S : String; const EscapePrefix : Char) : String;
  1203. Function  FindClosingQuote (const S : String; const OpenQuotePos : Integer = 1) : Integer;
  1204. Function  EncodeDotLineTerminated (const S : String) : String;
  1205. Function  EncodeEmptyLineTerminated (const S : String) : String;
  1206. Function  DecodeDotLineTerminated (const S : String) : String;
  1207. Function  DecodeEmptyLineTerminated (const S : String) : String;
  1208. Function  SplitQuotedList (const S : String; const Delimiter : String = ' ';
  1209.           const Quotes : CharSet = ['''', '"']) : StringArray;
  1210.  
  1211.  
  1212.  
  1213. {                                                                              }
  1214. { Natural language                                                             }
  1215. {                                                                              }
  1216. Function  Number (const Num : Int64; const USStyle : Boolean = False) : String; overload;
  1217. Function  Number (const Num : Extended; const USStyle : Boolean = False) : String; overload;
  1218. Function  StorageSize (const Bytes : Int64; const ShortFormat : Boolean = False) : String;
  1219. Function  TransferRate (const Bytes, MillisecondsElapsed : Int64; const ShortFormat : Boolean = False) : String;
  1220.  
  1221.  
  1222.  
  1223. {                                                                              }
  1224. { Pack/Unpack                                                                  }
  1225. {   Packs paramater (in its binary format) into a string                       }
  1226. {                                                                              }
  1227. Function  Pack (const D : Int64) : String; overload;
  1228. Function  Pack (const D : Integer) : String; overload;
  1229. Function  Pack (const D : Byte) : String; overload;
  1230. Function  Pack (const D : ShortInt) : String; overload;
  1231. Function  Pack (const D : SmallInt) : String; overload;
  1232. Function  Pack (const D : Word) : String; overload;
  1233. Function  Pack (const D : String) : String; overload;
  1234. Function  PackShortString (const D : ShortString) : String;
  1235. Function  Pack (const D : Extended) : String; overload;
  1236. Function  PackSingle (const D : Single) : String;
  1237. Function  PackDouble (const D : Double) : String;
  1238. Function  PackCurrency (const D : Currency) : String;
  1239. Function  PackDateTime (const D : TDateTime) : String;
  1240. Function  Pack (const D : Boolean) : String; overload;
  1241.  
  1242. Function  UnpackInteger (const D : String) : Integer;
  1243. Function  UnpackSingle (const D : String) : Single;
  1244. Function  UnpackDouble (const D : String) : Double;
  1245. Function  UnpackExtended (const D : String) : Extended;
  1246. Function  UnpackBoolean (const D : String) : Boolean;
  1247. Function  UnpackDateTime (const D : String) : TDateTime;
  1248. Function  UnpackString (const D : String) : String;
  1249. Function  UnpackShortString (const D : String) : ShortString;
  1250.  
  1251.  
  1252.  
  1253. {                                                                              }
  1254. { PChar routines                                                               }
  1255. {                                                                              }
  1256. Function  MatchString (const P : PChar; const S : String; const CaseSensitive : Boolean = True) : Boolean;
  1257. Function  SkipChar (var P : PChar; const C : Char) : Boolean; overload;
  1258. Function  SkipChar (var P : PChar; const C : CharSet) : Boolean; overload;
  1259. Function  SkipAll (var P : PChar; const C : Char) : Integer; overload;
  1260. Function  SkipAll (var P : PChar; const C : CharSet) : Integer; overload;
  1261. Function  SkipSeq (var P : PChar; const S1, S2 : CharSet) : Boolean; overload;
  1262. Function  SkipSeq (var P : PChar; const S1, S2, S3 : CharSet) : Boolean; overload;
  1263. Function  SkipString (var P : PChar; const S : String; const CaseSensitive : Boolean = True) : Boolean;
  1264. Function  ExtractAll (var P : PChar; const C : Char) : String; overload;
  1265. Function  ExtractAll (var P : PChar; const C : CharSet) : String; overload;
  1266. Function  ExtractTo (var P : PChar; const C : CharSet) : String; overload;
  1267. Function  ExtractTo (var P : PChar; const S : String; const CaseSensitive : Boolean = True) : String; overload;
  1268.  
  1269.  
  1270.  
  1271. {                                                                              }
  1272. { Dynamic array functions                                                      }
  1273. {                                                                              }
  1274. Function  StringArrayLength (const S : Array of String) : Integer;
  1275. Function  LongestStringLength (const S : Array of String) : Integer;
  1276.  
  1277. Function  Append (var V : CharSetArray; const S : String; const CaseSensitive : Boolean = True) : Integer; overload;
  1278.  
  1279. Function  PosNext (const Find : String; const V : StringArray; const PrevPos : Integer;
  1280.           const IsSortedAscending : Boolean; const CaseSensitive : Boolean) : Integer; overload;
  1281.  
  1282. Function  SingleArrayToStringArray (const V : SingleArray) : StringArray;
  1283. Function  DoubleArrayToStringArray (const V : DoubleArray) : StringArray;
  1284. Function  ExtendedArrayToStringArray (const V : ExtendedArray) : StringArray;
  1285. Function  LongIntArrayToStringArray (const V : LongIntArray) : StringArray;
  1286. Function  Int64ArrayToStringArray (const V : Int64Array) : StringArray;
  1287. Function  StringArrayToLongIntArray (const V : StringArray) : LongIntArray;
  1288. Function  StringArrayToInt64Array (const V : StringArray) : Int64Array;
  1289. Function  StringArrayToSingleArray (const V : StringArray) : SingleArray;
  1290. Function  StringArrayToDoubleArray (const V : StringArray) : DoubleArray;
  1291. Function  StringArrayToExtendedArray (const V : StringArray) : ExtendedArray;
  1292.  
  1293. Function  ByteArrayToStr (const V : ByteArray; const ItemDelimiter : String = ',') : String;
  1294. Function  WordArrayToStr (const V : WordArray; const ItemDelimiter : String = ',') : String;
  1295. Function  LongWordArrayToStr (const V : LongWordArray; const ItemDelimiter : String = ',') : String;
  1296. Function  CardinalArrayToStr (const V : CardinalArray; const ItemDelimiter : String = ',') : String;
  1297. Function  ShortIntArrayToStr (const V : ShortIntArray; const ItemDelimiter : String = ',') : String;
  1298. Function  SmallIntArrayToStr (const V : SmallIntArray; const ItemDelimiter : String = ',') : String;
  1299. Function  LongIntArrayToStr (const V : LongIntArray; const ItemDelimiter : String = ',') : String;
  1300. Function  IntegerArrayToStr (const V : IntegerArray; const ItemDelimiter : String = ',') : String;
  1301. Function  Int64ArrayToStr (const V : Int64Array; const ItemDelimiter : String = ',') : String;
  1302. Function  SingleArrayToStr (const V : SingleArray; const ItemDelimiter : String = ',') : String;
  1303. Function  DoubleArrayToStr (const V : DoubleArray; const ItemDelimiter : String = ',') : String;
  1304. Function  ExtendedArrayToStr (const V : ExtendedArray; const ItemDelimiter : String = ',') : String;
  1305. Function  StringArrayToStr (const V : StringArray; const ItemDelimiter : String = ',';
  1306.           const QuoteItems : Boolean = True; const Quote : Char = '''') : String;
  1307.  
  1308. Function  StrToByteArray (const S : String; const Delimiter : Char = ',') : ByteArray;
  1309. Function  StrToWordArray (const S : String; const Delimiter : Char = ',') : WordArray;
  1310. Function  StrToLongWordArray (const S : String; const Delimiter : Char = ',') : LongWordArray;
  1311. Function  StrToCardinalArray (const S : String; const Delimiter : Char = ',') : CardinalArray;
  1312. Function  StrToShortIntArray (const S : String; const Delimiter : Char = ',') : ShortIntArray;
  1313. Function  StrToSmallIntArray (const S : String; const Delimiter : Char = ',') : SmallIntArray;
  1314. Function  StrToLongIntArray (const S : String; const Delimiter : Char = ',') : LongIntArray;
  1315. Function  StrToIntegerArray (const S : String; const Delimiter : Char = ',') : IntegerArray;
  1316. Function  StrToInt64Array (const S : String; const Delimiter : Char = ',') : Int64Array;
  1317. Function  StrToSingleArray (const S : String; const Delimiter : Char = ',') : SingleArray;
  1318. Function  StrToDoubleArray (const S : String; const Delimiter : Char = ',') : DoubleArray;
  1319. Function  StrToExtendedArray (const S : String; const Delimiter : Char = ',') : ExtendedArray;
  1320. Function  StrToStringArray (const S : String; const Delimiter : Char = ',') : StringArray;
  1321.  
  1322.  
  1323.  
  1324. {                                                                              }
  1325. { Miscellaneous                                                                }
  1326. {                                                                              }
  1327. Function  Reversed (const S : String) : String;
  1328. Function  WithSuffix (const S : String; const Suffix : String;
  1329.           const CaseSensitive : Boolean = True) : String;
  1330. Function  WithPrefix (const S : String; const Prefix : String;
  1331.           const CaseSensitive : Boolean = True) : String;
  1332. Function  WithoutSuffix (const S : String; const Suffix : String;
  1333.           const CaseSensitive : Boolean = True) : String;
  1334. Function  WithoutPrefix (const S : String; const Prefix : String;
  1335.           const CaseSensitive : Boolean = True) : String;
  1336. Procedure EnsureSuffix (var S : String; const Suffix : String;
  1337.           const CaseSensitive : Boolean = True);
  1338. Procedure EnsurePrefix (var S : String; const Prefix : String;
  1339.           const CaseSensitive : Boolean = True);
  1340. Procedure EnsureNoSuffix (var S : String; const Suffix : String;
  1341.           const CaseSensitive : Boolean = True);
  1342. Procedure EnsureNoPrefix (var S : String; const Prefix : String;
  1343.           const CaseSensitive : Boolean = True);
  1344. Procedure SetLengthAndZero (var S : String; const NewLength : Integer); overload;
  1345.  
  1346.  
  1347.  
  1348. {                                                                              }
  1349. { Self testing code                                                            }
  1350. {                                                                              }
  1351. Procedure SelfTest;
  1352.  
  1353.  
  1354.  
  1355. implementation
  1356.  
  1357.  
  1358.  
  1359. {                                                                              }
  1360. { Type conversion                                                              }
  1361. {                                                                              }
  1362. Function StrToFloatDef (const S : String; const Default : Extended) : Extended;
  1363.   Begin
  1364.     try
  1365.       Result := StrToFloat (S);
  1366.     except
  1367.       Result := Default;
  1368.     end;
  1369.   End;
  1370.  
  1371. Function BooleanToStr (const B : Boolean) : String;
  1372.   Begin
  1373.     if B then
  1374.       Result := 'True' else
  1375.       Result := 'False';
  1376.   End;
  1377.  
  1378. Function StrToBoolean (const S : String) : Boolean;
  1379.   Begin
  1380.     Result := IsEqualNoCase (S, 'True');
  1381.   End;
  1382.  
  1383. Function TVarRecToString (const V : TVarRec; const QuoteStrings : Boolean) : String;
  1384.   Begin
  1385.     With V do
  1386.       Case VType of
  1387.         vtInteger    : Result := IntToStr (VInteger);
  1388.         vtInt64      : Result := IntToStr (VInt64^);
  1389.         vtChar       : Result := VChar;
  1390.         vtString     : Result := VString^;
  1391.         vtPChar      : Result := VPChar;
  1392.         vtAnsiString : Result := String (VAnsiString);
  1393.         vtExtended   : Result := FloatToStr (VExtended^);
  1394.         vtBoolean    : Result := BooleanToStr (VBoolean);
  1395.         vtObject     : Result := ObjectToStr (VObject);
  1396.         vtClass      : Result := ClassToStr (VClass);
  1397.         vtCurrency   : Result := CurrToStr (VCurrency^);
  1398.         vtVariant    : Result := String (VVariant^);
  1399.       end;
  1400.     if QuoteStrings and (V.VType in [vtChar, vtString, vtPChar, vtAnsiString]) then
  1401.       Result := QuoteText (Result);
  1402.   End;
  1403.  
  1404.  
  1405.  
  1406. {                                                                              }
  1407. { Miscellaneous                                                                }
  1408. {                                                                              }
  1409. {$IFDEF WINTEL}
  1410. Function LowCase (Ch : Char) : Char;
  1411.   Asm
  1412.             CMP     AL,'A'
  1413.             JB      @@exit
  1414.             CMP     AL,'Z'
  1415.             JA      @@exit
  1416.             ADD     AL,'a' - 'A'
  1417.     @@exit:
  1418.   End;
  1419. {$ELSE}
  1420. Function LowCase (Ch : Char) : Char;
  1421.   Begin
  1422.     if Ch in ['A'..'Z'] then
  1423.       Result := Char (Byte (Ch) - 32) else
  1424.       Result := Ch;
  1425.   End;
  1426. {$ENDIF}
  1427.  
  1428. {$IFDEF WINTEL}
  1429. Procedure ConvertUpper (var S : String);
  1430.   Asm
  1431.         OR      EAX, EAX
  1432.         JZ      @Exit
  1433.         PUSH    EAX
  1434.         MOV     EAX, [EAX]
  1435.         OR      EAX, EAX
  1436.         JZ      @ExitP
  1437.         MOV     ECX, [EAX - 4]
  1438.         OR      ECX, ECX
  1439.         JZ      @ExitP
  1440.         XOR     DH, DH
  1441.     @L2:
  1442.         DEC     ECX
  1443.         MOV     DL, [EAX + ECX]
  1444.         CMP     DL, 'a'
  1445.         JB      @L1
  1446.         CMP     DL, 'z'
  1447.         JA      @L1
  1448.         OR      DH, DH
  1449.         JZ      @Uniq
  1450.     @L3:
  1451.         SUB     DL, 'a' - 'A'
  1452.         MOV     [EAX + ECX], DL
  1453.     @L1:
  1454.         OR      ECX, ECX
  1455.         JNZ     @L2
  1456.         OR      DH, DH
  1457.         JNZ     @Exit
  1458.     @ExitP:
  1459.         POP     EAX
  1460.     @Exit:
  1461.         RET
  1462.     @Uniq:
  1463.         POP     EAX
  1464.         PUSH    ECX
  1465.         PUSH    EDX
  1466.         CALL    UniqueString
  1467.         POP     EDX
  1468.         POP     ECX
  1469.         MOV     DH, 1
  1470.         JMP     @L3
  1471.   End;
  1472. {$ELSE}
  1473. Procedure ConvertUpper (var S : String);
  1474. var F : Integer;
  1475.   Begin
  1476.     For F := 0 to Length (S) - 1 do
  1477.       if S [F] in ['a'..'z'] then
  1478.         S [F] := Char (Ord (S [F]) - 32);
  1479.   End;
  1480. {$ENDIF}
  1481.  
  1482. {$IFDEF WINTEL}
  1483. Procedure ConvertLower (var S : String);
  1484.   Asm
  1485.         OR      EAX, EAX
  1486.         JZ      @Exit
  1487.         PUSH    EAX
  1488.         MOV     EAX, [EAX]
  1489.         OR      EAX, EAX
  1490.         JZ      @ExitP
  1491.         MOV     ECX, [EAX - 4]
  1492.         OR      ECX, ECX
  1493.         JZ      @ExitP
  1494.         XOR     DH, DH
  1495.     @L2:
  1496.         DEC     ECX
  1497.         MOV     DL, [EAX + ECX]
  1498.         CMP     DL, 'A'
  1499.         JB      @L1
  1500.         CMP     DL, 'Z'
  1501.         JA      @L1
  1502.         OR      DH, DH
  1503.         JZ      @Uniq
  1504.     @L3:
  1505.         ADD     DL, 'a' - 'A'
  1506.         MOV     [EAX + ECX], DL
  1507.     @L1:
  1508.         OR      ECX, ECX
  1509.         JNZ     @L2
  1510.         OR      DH, DH
  1511.         JNZ     @Exit
  1512.     @ExitP:
  1513.         POP     EAX
  1514.     @Exit:
  1515.         RET
  1516.     @Uniq:
  1517.         POP     EAX
  1518.         PUSH    ECX
  1519.         PUSH    EDX
  1520.         CALL    UniqueString
  1521.         POP     EDX
  1522.         POP     ECX
  1523.         MOV     DH, 1
  1524.         JMP     @L3
  1525.   End;
  1526. {$ELSE}
  1527. Procedure ConvertLower (var S : String);
  1528. var F : Integer;
  1529.   Begin
  1530.     For F := 1 to Length (S) do
  1531.       if S [F] in ['A'..'Z'] then
  1532.         S [F] := Char (Ord (S [F]) + 32);
  1533.   End;
  1534. {$ENDIF}
  1535.  
  1536. {$IFDEF WINTEL}
  1537. Procedure ConvertFirstUp (var S : String);
  1538.   Asm
  1539.         TEST    EAX, EAX
  1540.         JZ      @Exit
  1541.         MOV     EDX, [EAX]
  1542.         TEST    EDX, EDX
  1543.         JZ      @Exit
  1544.         MOV     ECX, [EDX - 4]
  1545.         OR      ECX, ECX
  1546.         JZ      @Exit
  1547.         MOV     DL, [EDX]
  1548.         CMP     DL, 'a'
  1549.         JB      @Exit
  1550.         CMP     DL, 'z'
  1551.         JA      @Exit
  1552.         CALL    UniqueString
  1553.         SUB     BYTE PTR [EAX], 'a' - 'A'
  1554.     @Exit:
  1555.   End;
  1556. {$ELSE}
  1557. Procedure ConvertFirstUp (var S : String);
  1558. var P : PChar;
  1559.   Begin
  1560.     if S <> '' then
  1561.       begin
  1562.         P := Pointer (S);
  1563.         if P^ in ['a'..'z'] then
  1564.           S [1] := UpCase (P^);
  1565.       end;
  1566.   End;
  1567. {$ENDIF}
  1568.  
  1569. Function FirstUp (const S : String) : String;
  1570.   Begin
  1571.     Result := S;
  1572.     ConvertFirstUp (Result);
  1573.   End;
  1574.  
  1575. Procedure ConvertUpper (var S : StringArray);
  1576. var I : Integer;
  1577.   Begin
  1578.     For I := 0 to Length (S) - 1 do
  1579.       ConvertUpper (S [I]);
  1580.   End;
  1581.  
  1582. Procedure ConvertLower (var S : StringArray);
  1583. var I : Integer;
  1584.   Begin
  1585.     For I := 0 to Length (S) - 1 do
  1586.       ConvertLower (S [I]);
  1587.   End;
  1588.  
  1589.  
  1590.  
  1591. {                                                                              }
  1592. { Character class strings                                                      }
  1593. {                                                                              }
  1594. Function CharSetToCharClassStr (const C : CharSet) : String;
  1595.  
  1596.   Function ChStr (const Ch : Char) : String;
  1597.     Begin
  1598.       Case Ch of
  1599.         '\'        : Result := '\\';
  1600.         ']'        : Result := '\]';
  1601.         ASCII_BEL  : Result := '\a';
  1602.         ASCII_BS   : Result := '\b';
  1603.         ASCII_ESC  : Result := '\e';
  1604.         ASCII_FF   : Result := '\f';
  1605.         ASCII_LF   : Result := '\n';
  1606.         ASCII_CR   : Result := '\r';
  1607.         ASCII_HT   : Result := '\t';
  1608.         ASCII_VT   : Result := '\v';
  1609.         else if (Ch < #32) or (Ch > #127) then // non-printable
  1610.           Result := '\x' + IntToHex (Ord (Ch), 1) else
  1611.           Result := Ch;
  1612.       end;
  1613.     End;
  1614.  
  1615.   Function SeqStr (const SeqStart, SeqEnd : Char) : String;
  1616.     Begin
  1617.       Result := ChStr (SeqStart);
  1618.       if Ord (SeqEnd) = Ord (SeqStart) + 1 then
  1619.         Result := Result + ChStr (SeqEnd) else // consequetive chars
  1620.         if SeqEnd > SeqStart then // range
  1621.           Result := Result + '-' + ChStr (SeqEnd);
  1622.     End;
  1623.  
  1624. var CS : CharSet;
  1625.     F : Char;
  1626.     SeqStart : Char;
  1627.     Seq : Boolean;
  1628.  
  1629.   Begin
  1630.     if IsComplete (C) then
  1631.       Result := '.' else
  1632.     if IsEmpty (C) then
  1633.       Result := '[]' else
  1634.       begin
  1635.         Result := '[';
  1636.         CS := C;
  1637.         if (#0 in C) and (#255 in C) then
  1638.           begin
  1639.             ComplementCharSet (CS);
  1640.             Result := Result + '^';
  1641.           end;
  1642.         Seq := False;
  1643.         SeqStart := #0;
  1644.         For F := #0 to #255 do
  1645.           if F in CS then
  1646.             begin
  1647.               if not Seq then
  1648.                 begin
  1649.                   SeqStart := F;
  1650.                   Seq := True;
  1651.                 end;
  1652.             end else
  1653.             if Seq then
  1654.               begin
  1655.                 Result := Result + SeqStr (SeqStart, Char (Ord (F) - 1));
  1656.                 Seq := False;
  1657.               end;
  1658.         if Seq then
  1659.           Result := Result + SeqStr (SeqStart, #255);
  1660.         Result := Result + ']';
  1661.       end;
  1662.   End;
  1663.  
  1664. Function CharClassStrToCharSet (const S : String) : CharSet;
  1665. var I, L : Integer;
  1666.  
  1667.   Function DecodeChar : Char;
  1668.   var J : Integer;
  1669.     Begin
  1670.       if S [I] = '\' then
  1671.         if I + 1 = L then
  1672.           begin
  1673.             Inc (I);
  1674.             Result := '\';
  1675.           end else
  1676.           if not MatchQuantSeq (J, [['x'], cs_HexDigit, cs_HexDigit],
  1677.                                  [mqOnce, mqOnce, mqOptional], S, [moDeterministic], I + 1) then
  1678.             begin
  1679.               Case S [I + 1] of
  1680.                 '0' : Result := ASCII_NULL;
  1681.                 'a' : Result := ASCII_BEL;
  1682.                 'b' : Result := ASCII_BS;
  1683.                 'e' : Result := ASCII_ESC;
  1684.                 'f' : Result := ASCII_FF;
  1685.                 'n' : Result := ASCII_LF;
  1686.                 'r' : Result := ASCII_CR;
  1687.                 't' : Result := ASCII_HT;
  1688.                 'v' : Result := ASCII_VT;
  1689.                 else Result := S [I + 1];
  1690.               end;
  1691.               Inc (I, 2);
  1692.             end else
  1693.             begin
  1694.               if J = I + 2 then
  1695.                 Result := Char (HexDigitValue (S [J])) else
  1696.                 Result := Char (HexDigitValue (S [J - 1]) * 16 + HexDigitValue (S [J]));
  1697.               I := J + 1;
  1698.             end
  1699.       else
  1700.         begin
  1701.           Result := S [I];
  1702.           Inc (I);
  1703.         end;
  1704.     End;
  1705.  
  1706. var Neg : Boolean;
  1707.     A, B : Char;
  1708.   Begin
  1709.     L := Length (S);
  1710.     if (L = 0) or (S = '[]') then
  1711.       Result := [] else
  1712.     if L = 1 then
  1713.       if S [1] in ['.', '*', '?'] then
  1714.         Result := CompleteCharSet else
  1715.         Result := [S [1]] else
  1716.     if (S [1] <> '[') or (S [L] <> ']') then
  1717.       raise EConvertError.Create ('Invalid character class string') else
  1718.       begin
  1719.         Neg := S [2] = '^';
  1720.         I := iif (Neg, 3, 2);
  1721.         Result := [];
  1722.         While I < L do
  1723.           begin
  1724.             A := DecodeChar;
  1725.             if (I + 1 < L) and (S [I] = '-') then
  1726.               begin
  1727.                 Inc (I);
  1728.                 B := DecodeChar;
  1729.                 Result := Result + [A..B];
  1730.               end else
  1731.               Include (Result, A);
  1732.          end;
  1733.         if Neg then
  1734.           ComplementCharSet (Result);
  1735.       end;
  1736.   End;
  1737.  
  1738.  
  1739.  
  1740. {                                                                              }
  1741. { Dup                                                                          }
  1742. {                                                                              }
  1743. Function DupBuf (const Buf; const BufSize : Integer; const Count : Integer) : String;
  1744. var P : PChar;
  1745.     I : Integer;
  1746.   Begin
  1747.     if (Count <= 0) or (BufSize <= 0) then
  1748.       Result := '' else
  1749.       begin
  1750.         SetLength (Result, Count * BufSize);
  1751.         P := Pointer (Result);
  1752.         For I := 1 to Count do
  1753.           begin
  1754.             MoveMem (Buf, P^, BufSize);
  1755.             Inc (P, BufSize);
  1756.           end;
  1757.       end;
  1758.   End;
  1759.  
  1760. Function DupBuf (const Buf; const BufSize : Integer) : String;
  1761.   Begin
  1762.     if BufSize <= 0 then
  1763.       Result := '' else
  1764.       begin
  1765.         SetLength (Result, BufSize);
  1766.         MoveMem (Buf, Pointer (Result)^, BufSize);
  1767.       end;
  1768.   End;
  1769.  
  1770. Function Dup (const S : String; const Count : Integer) : String;
  1771. var L : Integer;
  1772.   Begin
  1773.     L := Length (S);
  1774.     if L = 0 then
  1775.       Result := '' else
  1776.       Result := DupBuf (Pointer (S)^, L, Count);
  1777.   End;
  1778.  
  1779. Function Dup (const Ch : Char; const Count : Integer) : String;
  1780.   Begin
  1781.     if Count <= 0 then
  1782.       begin
  1783.         Result := '';
  1784.         exit;
  1785.       end;
  1786.     SetLength (Result, Count);
  1787.     FillChar (Pointer (Result)^, Count, Ch);
  1788.   End;
  1789.  
  1790.  
  1791.  
  1792. {                                                                              }
  1793. { Copy                                                                         }
  1794. {                                                                              }
  1795. Function CopyRange (const S : String; const StartIndex, StopIndex : Integer) : String;
  1796. var L, I : Integer;
  1797.   Begin
  1798.     L := Length (S);
  1799.     if (StartIndex > StopIndex) or (StopIndex < 1) or (StartIndex > L) or (L = 0) then
  1800.       Result := '' else
  1801.       begin
  1802.         if StartIndex <= 1 then
  1803.           if StopIndex >= L then
  1804.             begin
  1805.               Result := S;
  1806.               exit;
  1807.             end else
  1808.             I := 1
  1809.         else I := StartIndex;
  1810.         Result := Copy (S, I, StopIndex - I + 1);
  1811.       end;
  1812.   End;
  1813.  
  1814. Function CopyFrom (const S : String; const StartIndex : Integer) : String;
  1815. var L : Integer;
  1816.   Begin
  1817.     if StartIndex <= 1 then
  1818.       Result := S else
  1819.       begin
  1820.         L := Length (S);
  1821.         if (L = 0) or (StartIndex > L) then
  1822.           Result := '' else
  1823.           Result := Copy (S, StartIndex, L - StartIndex + 1);
  1824.       end;
  1825.   End;
  1826.  
  1827. Function CopyLeft (const S : String; const Count : Integer) : String;
  1828. var L : Integer;
  1829.   Begin
  1830.     L := Length (S);
  1831.     if (L = 0) or (Count <= 0) then
  1832.       Result := '' else
  1833.       if Count >= L then
  1834.         Result := S else
  1835.         Result := Copy (S, 1, Count);
  1836.   End;
  1837.  
  1838. Function CopyRight (const S : String; const Count : Integer) : String;
  1839. var L : Integer;
  1840.   Begin
  1841.     L := Length (S);
  1842.     if (L = 0) or (Count <= 0) then
  1843.       Result := '' else
  1844.       if Count >= L then
  1845.         Result := S else
  1846.         Result := Copy (S, L - Count + 1, Count);
  1847.   End;
  1848.  
  1849. Function CutLeft (var S : String; const Count : Integer) : String;
  1850.   Begin
  1851.     if Count <= 0 then
  1852.       Result := '' else
  1853.       begin
  1854.         Result := CopyLeft (S, Count);
  1855.         Delete (S, 1, Count);
  1856.       end;
  1857.   End;
  1858.  
  1859. Function CutRight (var S : String; const Count : Integer) : String;
  1860.   Begin
  1861.     if Count <= 0 then
  1862.       Result := '' else
  1863.       begin
  1864.         Result := CopyRight (S, Count);
  1865.         SetLength (S, MaxI (0, Length (S) - Count));
  1866.       end;
  1867.   End;
  1868.  
  1869. Function Cut (var S : String; const Index, Count : Integer) : String;
  1870. var L, I, C : Integer;
  1871.   Begin
  1872.     L := Length (S);
  1873.     if (L = 0) or (Count <= 0) or (Index > L) then
  1874.       begin
  1875.         Result := '';
  1876.         exit;
  1877.       end;
  1878.     I := Index;
  1879.     C := Count;
  1880.     if I <= 0 then
  1881.       begin
  1882.         Inc (C, I - 1);
  1883.         if C <= 0 then
  1884.           begin
  1885.             Result := '';
  1886.             exit;
  1887.           end;
  1888.         I := 1;
  1889.       end;
  1890.     Result := Copy (S, I, C);
  1891.     Delete (S, I, C);
  1892.   End;
  1893.  
  1894. Function CutTo (var S : String; const Delimiter : Char; const DelimiterOptional : Boolean; const FindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const Count : Integer) : String;
  1895. var I : Integer;
  1896.   Begin
  1897.     Result := CopyTo (S, Delimiter, DelimiterOptional, FindOptions, Start, Stop, Count);
  1898.     I := Length (Result);
  1899.     if I > 0 then
  1900.       Delete (S, 1, I);
  1901.   End;
  1902.  
  1903.  
  1904.  
  1905. {                                                                              }
  1906. { Match                                                                        }
  1907. {                                                                              }
  1908. var
  1909.   LowCaseLookup : Array [#0..#255] of Char = (
  1910.     #$00, #$01, #$02, #$03, #$04, #$05, #$06, #$07, #$08, #$09, #$0A, #$0B, #$0C, #$0D, #$0E, #$0F,
  1911.     #$10, #$11, #$12, #$13, #$14, #$15, #$16, #$17, #$18, #$19, #$1A, #$1B, #$1C, #$1D, #$1E, #$1F,
  1912.     #$20, #$21, #$22, #$23, #$24, #$25, #$26, #$27, #$28, #$29, #$2A, #$2B, #$2C, #$2D, #$2E, #$2F,
  1913.     #$30, #$31, #$32, #$33, #$34, #$35, #$36, #$37, #$38, #$39, #$3A, #$3B, #$3C, #$3D, #$3E, #$3F,
  1914.     #$40, #$61, #$62, #$63, #$64, #$65, #$66, #$67, #$68, #$69, #$6A, #$6B, #$6C, #$6D, #$6E, #$6F,
  1915.     #$70, #$71, #$72, #$73, #$74, #$75, #$76, #$77, #$78, #$79, #$7A, #$5B, #$5C, #$5D, #$5E, #$5F,
  1916.     #$60, #$61, #$62, #$63, #$64, #$65, #$66, #$67, #$68, #$69, #$6A, #$6B, #$6C, #$6D, #$6E, #$6F,
  1917.     #$70, #$71, #$72, #$73, #$74, #$75, #$76, #$77, #$78, #$79, #$7A, #$7B, #$7C, #$7D, #$7E, #$7F,
  1918.     #$80, #$81, #$82, #$83, #$84, #$85, #$86, #$87, #$88, #$89, #$8A, #$8B, #$8C, #$8D, #$8E, #$8F,
  1919.     #$90, #$91, #$92, #$93, #$94, #$95, #$96, #$97, #$98, #$99, #$9A, #$9B, #$9C, #$9D, #$9E, #$9F,
  1920.     #$A0, #$A1, #$A2, #$A3, #$A4, #$A5, #$A6, #$A7, #$A8, #$A9, #$AA, #$AB, #$AC, #$AD, #$AE, #$AF,
  1921.     #$B0, #$B1, #$B2, #$B3, #$B4, #$B5, #$B6, #$B7, #$B8, #$B9, #$BA, #$BB, #$BC, #$BD, #$BE, #$BF,
  1922.     #$C0, #$C1, #$C2, #$C3, #$C4, #$C5, #$C6, #$C7, #$C8, #$C9, #$CA, #$CB, #$CC, #$CD, #$CE, #$CF,
  1923.     #$D0, #$D1, #$D2, #$D3, #$D4, #$D5, #$D6, #$D7, #$D8, #$D9, #$DA, #$DB, #$DC, #$DD, #$DE, #$DF,
  1924.     #$E0, #$E1, #$E2, #$E3, #$E4, #$E5, #$E6, #$E7, #$E8, #$E9, #$EA, #$EB, #$EC, #$ED, #$EE, #$EF,
  1925.     #$F0, #$F1, #$F2, #$F3, #$F4, #$F5, #$F6, #$F7, #$F8, #$F9, #$FA, #$FB, #$FC, #$FD, #$FE, #$FF);
  1926.  
  1927. {$IFDEF WINTEL}
  1928. Function MatchNoCase (const A, B : Char) : Boolean;
  1929.   Asm
  1930.       and eax, $000000FF
  1931.       and edx, $000000FF
  1932.       mov al, byte ptr [LowCaseLookup + eax]
  1933.       cmp al, byte ptr [LowCaseLookup + edx]
  1934.       setz al
  1935.   End;
  1936. {$ELSE}
  1937. Function MatchNoCase (const A, B : Char) : Boolean;
  1938.   Begin
  1939.     Result := LowCaseLookup [A] = LowCaseLookup [B];
  1940.   End;
  1941. {$ENDIF}
  1942.  
  1943. {$IFDEF WINTEL}
  1944. Function Match (const A, B : Char; const CaseSensitive : Boolean) : Boolean;
  1945.   Asm
  1946.       or cl, cl
  1947.       jz MatchNoCase
  1948.       cmp al, dl
  1949.       setz al
  1950.   End;
  1951. {$ELSE}
  1952. Function Match (const A, B : Char; const CaseSensitive : Boolean) : Boolean;
  1953.   Begin
  1954.     if CaseSensitive then
  1955.       Result := A = B else
  1956.       Result := LowCaseLookup [A] = LowCaseLookup [B];
  1957.   End;
  1958. {$ENDIF}
  1959.  
  1960. {$IFDEF WINTEL}
  1961. const
  1962.   ModMaskLookup : Array [0..2] of LongWord = ($000000FF, $0000FFFF, $00FFFFFF);
  1963.  
  1964. Function Match (const M, S : String; const StartIndex : Integer; const CaseSensitive : Boolean) : Boolean;
  1965.   Asm
  1966.         push esi
  1967.         push edi
  1968.         push ebx                    // Save state
  1969.  
  1970.         mov edi, S                  // edi = S [1]
  1971.         or edi, edi
  1972.         jz @NoMatch                 // if S = '' then @NoMatch
  1973.  
  1974.         mov esi, M                  // esi = M [1]
  1975.         or esi, esi
  1976.         jz @NoMatch                 // if M = '' then @NoMatch
  1977.  
  1978.         mov edx, StartIndex
  1979.         cmp edx, 1
  1980.         js @NoMatch                 // if StartIndex < 1 then @NoMatch
  1981.  
  1982.         add edx, [esi - 4]
  1983.         dec edx                     // edx = StartIndex + Length (M) - 1
  1984.         cmp edx, [edi - 4]
  1985.         ja @NoMatch                 // if StartIndex + Length (M) - 1 > Length (S) then @NoMatch
  1986.  
  1987.         add edi, ecx
  1988.         dec edi                     // edi = S [StartIndex]
  1989.         mov ecx, [esi - 4]          // ecx = Length (M)
  1990.         or  ecx, ecx
  1991.         jz @NoMatch                 // If M = '' then @NoMatch
  1992.  
  1993.         mov bl, CaseSensitive       // bl = CaseSensitive
  1994.         or bl, bl
  1995.         jnz @CompareSensitive
  1996.  
  1997.         xor eax, eax
  1998.         xor edx, edx
  1999.       @NextInsensitive:
  2000.         mov al, [esi + ecx - 1]
  2001.         mov dl, [edi + ecx - 1]
  2002.         mov al, byte ptr [LowCaseLookup + eax]
  2003.         cmp al, byte ptr [LowCaseLookup + edx]
  2004.         jne @NoMatch
  2005.         dec ecx
  2006.         jnz @NextInsensitive
  2007.         jmp @Match
  2008.  
  2009.       @CompareSensitive:
  2010.       //     rep cmsb                                                         //
  2011.       //     je @Match                                                        //
  2012.         mov dl, cl                                                            //
  2013.         and edx, $00000003                                                    //
  2014.         shr ecx, 2                                                            //
  2015.         jz @CheckMod                 { Length (M) < 4 }                       //
  2016.                                                                               //
  2017.       {     rep cmpsd                  {}                                     //
  2018.       {     jne @NoMatch               {}                                     //
  2019.       @loop1:                          {}                                     //
  2020.         mov eax, [esi]                 {}                                     //
  2021.         cmp eax, [edi]                 {}                                     //
  2022.         jne @NoMatch                   {}                                     //
  2023.         add esi, 4                     {}                                     //
  2024.         add edi, 4                     {}                                     //
  2025.         dec ecx                        {}                                     //
  2026.         jnz @loop1                     {}                                     //
  2027.                                                                               //
  2028.         or dl, dl                                                             //
  2029.         jz @Match                                                             //
  2030.                                                                               //
  2031.       { Check remaining dl (0-3) bytes                 {}                     //
  2032.       @CheckMod:                                       {}                     //
  2033.         mov eax, [esi]                                 {}                     //
  2034.         mov ecx, [edi]                                 {}                     //
  2035.         dec edx                                        {}                     //
  2036.         shl edx, 2                                     {}                     //
  2037.         and eax, dword ptr [ModMaskLookup + edx]       {}                     //
  2038.         and ecx, dword ptr [ModMaskLookup + edx]       {}                     //
  2039.         cmp eax, ecx
  2040.         je @Match
  2041.  
  2042.       @NoMatch:
  2043.         xor al, al                  // Result := False
  2044.         jmp @Fin
  2045.  
  2046.       @Match:
  2047.         mov al, 1                   // Result := True
  2048.  
  2049.       @Fin:
  2050.         pop ebx                     // Restore state
  2051.         pop edi
  2052.         pop esi
  2053.   End;
  2054. {$ELSE}
  2055. Function Match (const M, S : String; const StartIndex : Integer; const CaseSensitive : Boolean) : Boolean;
  2056. var I, L : Integer;
  2057.   Begin
  2058.     L := Length (M);
  2059.     if (L = 0) or (L + StartIndex - 1 > Length (S)) then
  2060.       begin
  2061.         Result := False;
  2062.         exit;
  2063.       end;
  2064.     For I := 1 to L do
  2065.       if not Match (M [I], S [StartIndex + I - 1], CaseSensitive) then
  2066.         begin
  2067.           Result := False;
  2068.           exit;
  2069.         end;
  2070.     Result := True;
  2071.   End;
  2072. {$ENDIF}
  2073.  
  2074. Function MatchBuf (const M : String; const Buf; const BufSize : Integer; const CaseSensitive : Boolean) : Boolean;
  2075. var L, I : Integer;
  2076.     P, Q : PChar;
  2077.   Begin
  2078.     L := Length (M);
  2079.     if (L = 0) or (L > BufSize) then
  2080.       begin
  2081.         Result := False;
  2082.         exit;
  2083.       end;
  2084.     P := @Buf;
  2085.     Q := Pointer (M);
  2086.     if CaseSensitive then
  2087.       Result := CompareMem (P^, Q^, L) else
  2088.       begin
  2089.         For I := 1 to L do
  2090.           if not MatchNoCase (P^, Q^) then
  2091.             begin
  2092.               Result := False;
  2093.               exit;
  2094.             end else
  2095.             begin
  2096.               Inc (P);
  2097.               Inc (Q);
  2098.             end;
  2099.         Result := True;
  2100.       end;
  2101.   End;
  2102.  
  2103. Function Match (const A : CharSet; const B : Char; const CaseSensitive : Boolean) : Boolean;
  2104.   Begin
  2105.     if CaseSensitive then
  2106.       Result := B in A else
  2107.       Result := (UpCase (B) in A) or (LowCase (B) in A);
  2108.   End;
  2109.  
  2110. Function MatchCount (const M : Char; const S : String; const StartIndex : Integer; const MaxCount : Integer; const CaseSensitive : Boolean) : Integer;
  2111. var I, Start, Stop : Integer;
  2112.   Begin
  2113.     if MaxCount = 0 then
  2114.       Result := 0 else
  2115.       begin
  2116.         Start := MaxI (StartIndex, 1);
  2117.         if MaxCount < 0 then
  2118.           Stop := Length (S) else
  2119.           Stop := MinI (Start + MaxCount - 1, Length (S));
  2120.         Result := 0;
  2121.         For I := Start to Stop do
  2122.           if not Match (M, S [I], CaseSensitive) then
  2123.             exit else
  2124.             Inc (Result);
  2125.       end;
  2126.   End;
  2127.  
  2128. Function Match (const M : Char; const S : String; const StartIndex : Integer; const Count : Integer; const CaseSensitive : Boolean) : Boolean;
  2129. var StopIndex : Integer;
  2130.   Begin
  2131.     if Count <= 0 then
  2132.       Result := True else
  2133.       begin
  2134.         StopIndex := StartIndex + Count - 1;
  2135.         if (StartIndex < 1) or (StopIndex > Length (S)) then
  2136.           Result := False else
  2137.           Result := MatchCount (M, S, StartIndex, Count, CaseSensitive) = Count;
  2138.       end;
  2139.   End;
  2140.  
  2141. Function MatchCount (const M : CharSet; const S : String; const StartIndex : Integer; const MaxCount : Integer; const CaseSensitive : Boolean) : Integer;
  2142. var I, Start, Stop : Integer;
  2143.   Begin
  2144.     if MaxCount = 0 then
  2145.       Result := 0 else
  2146.       begin
  2147.         Start := MaxI (StartIndex, 1);
  2148.         if MaxCount < 0 then
  2149.           Stop := Length (S) else
  2150.           Stop := MinI (Start + MaxCount - 1, Length (S));
  2151.         Result := 0;
  2152.         For I := Start to Stop do
  2153.           if not Match (M, S [I], CaseSensitive) then
  2154.             exit else
  2155.             Inc (Result);
  2156.       end;
  2157.   End;
  2158.  
  2159. Function Match (const M : CharSet; const S : String; const StartIndex : Integer; const Count : Integer; const CaseSensitive : Boolean) : Boolean;
  2160. var StopIndex : Integer;
  2161.   Begin
  2162.     if Count <= 0 then
  2163.       Result := True else
  2164.       begin
  2165.         StopIndex := StartIndex + Count - 1;
  2166.         if (StartIndex < 1) or (StopIndex > Length (S)) then
  2167.           Result := False else
  2168.           Result := MatchCount (M, S, StartIndex, Count, CaseSensitive) = Count;
  2169.       end;
  2170.   End;
  2171.  
  2172. Function MatchSeq (const M : Array of CharSet; const S : String; const StartIndex : Integer = 1; const CaseSensitive : Boolean = True) : Boolean;
  2173. var J, C, L : Integer;
  2174.   Begin
  2175.     C := Length (M);
  2176.     L := Length (S);
  2177.     if (C = 0) or (StartIndex < 1) or (StartIndex + C - 1 > L) or (L = 0) then
  2178.       begin
  2179.         Result := False;
  2180.         exit;
  2181.       end;
  2182.  
  2183.     For J := 0 to C - 1 do
  2184.       if not Match (M [J], S [J + StartIndex], CaseSensitive) then
  2185.         begin
  2186.           Result := False;
  2187.           exit;
  2188.         end;
  2189.  
  2190.     Result := True;
  2191.   End;
  2192.  
  2193.  
  2194. Function MatchLeft (const M, S : String; const CaseSensitive : Boolean) : Boolean;
  2195.   Begin
  2196.     Result := Match (M, S, 1, CaseSensitive);
  2197.   End;
  2198.  
  2199. Function MatchRight (const M, S : String; const CaseSensitive : Boolean) : Boolean;
  2200.   Begin
  2201.     Result := Match (M, S, Length (S) - Length (M) + 1, CaseSensitive);
  2202.   End;
  2203.  
  2204. Function IsEqual (const A, B : String; const CaseSensitive : Boolean) : Boolean;
  2205. var L1, L2 : Integer;
  2206.   Begin
  2207.     L1 := Length (A);
  2208.     L2 := Length (B);
  2209.     Result := L1 = L2;
  2210.     if not Result or (L1 = 0) then
  2211.       exit;
  2212.     Result := Match (A, B, 1, CaseSensitive);
  2213.   End;
  2214.  
  2215. Function IsEqualNoCase (const A, B : String) : Boolean;
  2216. var L1, L2 : Integer;
  2217.   Begin
  2218.     L1 := Length (A);
  2219.     L2 := Length (B);
  2220.     Result := L1 = L2;
  2221.     if not Result or (L1 = 0) then
  2222.       exit;
  2223.     Result := Match (A, B, 1, False);
  2224.   End;
  2225.  
  2226. Function MatchChars (const M : Char; const S : Array of Char; const CaseSensitive : Boolean) : Integer;
  2227. var I : Integer;
  2228.   Begin
  2229.     For I := 0 to High (S) do
  2230.       if Match (M, S [I], CaseSensitive) then
  2231.         begin
  2232.           Result := I;
  2233.           exit;
  2234.         end;
  2235.     Result := -1;
  2236.   End;
  2237.  
  2238. Function MatchStrings (const M : String; const S : Array of String; const CaseSensitive : Boolean; const StartIndex : Integer; const MaxMatchLength : Integer) : Integer;
  2239. var I : Integer;
  2240.   Begin
  2241.     For I := 0 to High (S) do
  2242.       if ((MaxMatchLength < 0) or (Length (S [I]) <= MaxMatchLength)) and
  2243.          Match (S [I], M, StartIndex, CaseSensitive) then
  2244.         begin
  2245.           Result := I;
  2246.           exit;
  2247.         end;
  2248.     Result := -1;
  2249.   End;
  2250.  
  2251. Function MatchStrings (const M : Array of String; const S : Array of String; var MatchedItem : Integer; const CaseSensitive : Boolean; const MaxMatchLength : Integer) : Integer; overload;
  2252. var I : Integer;
  2253.   Begin
  2254.     For I := 0 to High (M) do
  2255.       begin
  2256.         Result := MatchStrings (M [I], S, CaseSensitive, 1, MaxMatchLength);
  2257.         if Result >= 0 then
  2258.           begin
  2259.             MatchedItem := I;
  2260.             exit;
  2261.           end;
  2262.       end;
  2263.     Result := -1;
  2264.     MatchedItem := -1;
  2265.   End;
  2266.  
  2267.  
  2268.  
  2269. {                                                                              }
  2270. { Abbreviated regular expression matcher                                       }
  2271. {                                                                              }
  2272. Function MatchQuantSeq (var MatchPos : Integer; const MatchSeq : Array of CharSet; const Quant : Array of TMatchQuantifier; const S : String; const MatchOptions : TMatchQuantSeqOptions; const StartIndex : Integer; const StopIndex : Integer) : Boolean;
  2273. var Stop : Integer;
  2274.     Deterministic, NonGreedy : Boolean;
  2275.  
  2276.   Function MatchAt (MPos, SPos : Integer; var MatchPos : Integer) : Boolean;
  2277.  
  2278.     Function MatchAndAdvance : Boolean;
  2279.     var I : Integer;
  2280.       Begin
  2281.         I := SPos;
  2282.         Result := S [I] in MatchSeq [MPos];
  2283.         if Result then
  2284.           begin
  2285.             MatchPos := I;
  2286.             Inc (SPos);
  2287.           end;
  2288.       End;
  2289.  
  2290.     Function MatchAndSetResult (var Res : Boolean) : Boolean;
  2291.       Begin
  2292.         Result := MatchAndAdvance;
  2293.         Res := Result;
  2294.         if not Result then
  2295.           MatchPos := 0;
  2296.       End;
  2297.  
  2298.     Function MatchAny : Boolean;
  2299.     var I, L : Integer;
  2300.         P : PChar;
  2301.       Begin
  2302.         L := Stop;
  2303.         if Deterministic then
  2304.           begin
  2305.             While (SPos <= L) and MatchAndAdvance do ;
  2306.             Result := False;
  2307.           end else
  2308.         if NonGreedy then
  2309.           Repeat
  2310.             Result := MatchAt (MPos + 1, SPos, MatchPos);
  2311.             if Result or not MatchAndAdvance then
  2312.               exit;
  2313.           Until SPos > L
  2314.         else
  2315.           begin
  2316.             I := SPos;
  2317.             P := Pointer (S);
  2318.             Inc (P, I - 1);
  2319.             While (I <= L) and (P^ in MatchSeq [MPos]) do
  2320.               begin
  2321.                 Inc (I);
  2322.                 Inc (P);
  2323.               end;
  2324.             Repeat
  2325.               MatchPos := I - 1;
  2326.               Result := MatchAt (MPos + 1, I, MatchPos);
  2327.               if Result then
  2328.                 exit;
  2329.               Dec (I);
  2330.             Until SPos > I;
  2331.           end;
  2332.       End;
  2333.  
  2334.   var Q : TMatchQuantifier;
  2335.       L, M : Integer;
  2336.     Begin
  2337.       L := Length (MatchSeq);
  2338.       M := Stop;
  2339.       While (MPos < L) and (SPos <= M) do
  2340.         begin
  2341.           Q := Quant [MPos];
  2342.           if Q in [mqOnce, mqLeastOnce] then
  2343.             if not MatchAndSetResult (Result) then
  2344.               exit;
  2345.           if (Q = mqAny) or ((Q = mqLeastOnce) and (SPos <= M)) then
  2346.             begin
  2347.               Result := MatchAny;
  2348.               if Result then
  2349.                 exit;
  2350.             end else
  2351.           if Q = mqOptional then
  2352.             if Deterministic then
  2353.               MatchAndAdvance else
  2354.               begin
  2355.                 if NonGreedy then
  2356.                   begin
  2357.                     Result := MatchAt (MPos + 1, SPos, MatchPos);
  2358.                     if Result or not MatchAndSetResult (Result) then
  2359.                       exit;
  2360.                   end else
  2361.                   begin
  2362.                     Result := (MatchAndAdvance and MatchAt (MPos + 1, SPos, MatchPos)) or
  2363.                               MatchAt (MPos + 1, SPos, MatchPos);
  2364.                     exit;
  2365.                   end;
  2366.               end;
  2367.           Inc (MPos);
  2368.         end;
  2369.       While (MPos < L) and (Quant [MPos] in [mqAny, mqOptional]) do
  2370.         Inc (MPos);
  2371.       Result := MPos = L;
  2372.       if not Result then
  2373.         MatchPos := 0;
  2374.     End;
  2375.  
  2376.   Begin
  2377.     Assert (Length (MatchSeq) = Length (Quant), 'MatchSeq and Quant not of equal length');
  2378.     if StopIndex < 0 then
  2379.       Stop := Length (S) else
  2380.       Stop := MinI (StopIndex, Length (S));
  2381.     MatchPos := 0;
  2382.     if (Length (MatchSeq) = 0) or (StartIndex > Stop) or (StartIndex <= 0) then
  2383.       begin
  2384.         Result := False;
  2385.         exit;
  2386.       end;
  2387.     NonGreedy := moNonGreedy in MatchOptions;
  2388.     Deterministic := moDeterministic in MatchOptions;
  2389.     Result := MatchAt (0, StartIndex, MatchPos);
  2390.   End;
  2391.  
  2392. Constructor TQuantSeq.Create (const MatchSeq : Array of CharSet; const Quant : Array of TMatchQuantifier; const MatchOptions : TMatchQuantSeqOptions);
  2393. var I, L : Integer;
  2394.   Begin
  2395.     Assert (Length (MatchSeq) = Length (Quant), 'Incomplete definition');
  2396.     inherited Create;
  2397.     L := Length (MatchSeq);
  2398.     SetLength (Sequence, L);
  2399.     SetLength (Quantity, L);
  2400.     For I := 0 to L - 1 do
  2401.       begin
  2402.         Sequence [I] := MatchSeq [I];
  2403.         Quantity [I] := Quant [I];
  2404.       end;
  2405.     Options := MatchOptions;
  2406.   End;
  2407.  
  2408. Procedure TQuantSeq.AddToSequence (const Ch : CharSet; const Quant : TMatchQuantifier);
  2409. var L : Integer;
  2410.   Begin
  2411.     Append (Sequence, Ch);
  2412.     L := Length (Quantity);
  2413.     SetLength (Quantity, L + 1);
  2414.     Quantity [L] := Quant;
  2415.   End;
  2416.  
  2417. Procedure TQuantSeq.AddStringToSequence (const S : String; const CaseSensitive : Boolean);
  2418. var I, L : Integer;
  2419.   Begin
  2420.     L := Append (Sequence, S, CaseSensitive);
  2421.     For I := 0 to Length (S) - 1 do
  2422.       Quantity [L + I] := mqOnce;
  2423.   End;
  2424.  
  2425. Function TQuantSeq.Match (var MatchPos : Integer; const S : String; const StartIndex : Integer; const StopIndex : Integer) : Boolean;
  2426.   Begin
  2427.     Result := MatchQuantSeq (MatchPos, Sequence, Quantity, S, Options, StartIndex, StopIndex);
  2428.   End;
  2429.  
  2430.  
  2431.  
  2432. {                                                                              }
  2433. { MatchPattern                                                                 }
  2434. {   Based on MatchPattern from a Delphi 3000 article by Paramjeet Reen         }
  2435. {   (http://www.delphi3000.com/articles/article_1561.asp).                     }
  2436. {                                                                              }
  2437. Function MatchPattern (M, S : PChar) : Boolean;
  2438.  
  2439.   Function EscapedChar (const C : Char) : Char;
  2440.     Begin
  2441.       Case C of
  2442.         'b' : Result := ASCII_BS;
  2443.         'e' : Result := ASCII_ESC;
  2444.         'f' : Result := ASCII_FF;
  2445.         'n' : Result := ASCII_LF;
  2446.         'r' : Result := ASCII_CR;
  2447.         't' : Result := ASCII_HT;
  2448.         'v' : Result := ASCII_VT;
  2449.         else Result := C;
  2450.       end;
  2451.     End;
  2452.  
  2453. var A, C, D : Char;
  2454.     N : Boolean;
  2455.   Begin
  2456.     Repeat
  2457.       Case M^ of
  2458.         #0 : // end of pattern
  2459.           begin
  2460.             Result := S^ = #0;
  2461.             exit;
  2462.           end;
  2463.         '?' : // match one
  2464.           if S^ = #0 then
  2465.             begin
  2466.               Result := False;
  2467.               exit;
  2468.             end else
  2469.             begin
  2470.               Inc (M);
  2471.               Inc (S);
  2472.             end;
  2473.         '*' :
  2474.           begin
  2475.             Inc (M);
  2476.             if M^ = #0 then // always match at end of mask
  2477.               begin
  2478.                 Result := True;
  2479.                 exit;
  2480.               end else
  2481.               while S^ <> #0 do
  2482.                 if MatchPattern (M, S) then
  2483.                   begin
  2484.                     Result := True;
  2485.                     Exit;
  2486.                   end else
  2487.                   Inc (S);
  2488.             end;
  2489.         '[' : // character class
  2490.           begin
  2491.             A := S^;
  2492.             Inc (M);
  2493.             C := M^;
  2494.             N := C = '^';
  2495.             Result := N;
  2496.             While C <> ']' do
  2497.               begin
  2498.                 if C = #0 then
  2499.                   begin
  2500.                     Result := False;
  2501.                     exit;
  2502.                   end;
  2503.                 Inc (M);
  2504.                 if C = '\' then // escaped character
  2505.                   begin
  2506.                     C := M^;
  2507.                     if C = #0 then
  2508.                       begin
  2509.                         Result := False;
  2510.                         exit;
  2511.                       end;
  2512.                     C := EscapedChar (C);
  2513.                     Inc (M);
  2514.                   end;
  2515.                 D := M^;
  2516.                 if D = '-' then // match range
  2517.                   begin
  2518.                     Inc (M);
  2519.                     D := M^;
  2520.                     if D = #0 then
  2521.                       begin
  2522.                         Result := False;
  2523.                         exit;
  2524.                       end;
  2525.                     if D = '\' then // escaped character
  2526.                       begin
  2527.                         Inc (M);
  2528.                         D := M^;
  2529.                         if D = #0 then
  2530.                           begin
  2531.                             Result := False;
  2532.                             exit;
  2533.                           end;
  2534.                         D := EscapedChar (D);
  2535.                         Inc (M);
  2536.                       end;
  2537.                     if (A >= C) and (A <= D) then
  2538.                       begin
  2539.                         Result := not N;
  2540.                         break;
  2541.                       end;
  2542.                     Inc (M);
  2543.                     C := M^;
  2544.                   end else
  2545.                   begin // match single character
  2546.                     if A = C then
  2547.                       begin
  2548.                         Result := not N;
  2549.                         break;
  2550.                       end;
  2551.                     C := D;
  2552.                   end;
  2553.               end;
  2554.             if not Result then
  2555.               exit;
  2556.             Inc (S);
  2557.             // Locate closing bracket
  2558.             While M^ <> ']' do
  2559.               if M^ = #0 then
  2560.                 begin
  2561.                   Result := False;
  2562.                   exit;
  2563.                 end else
  2564.                 Inc (M);
  2565.             Inc (M);
  2566.           end;
  2567.       else // single character match
  2568.         if M^ <> S^ then
  2569.           begin
  2570.             Result := False;
  2571.             exit;
  2572.           end else
  2573.           begin
  2574.             Inc (M);
  2575.             Inc (S);
  2576.           end;
  2577.       end;
  2578.     Until False;
  2579.   End;
  2580.  
  2581.  
  2582.  
  2583. {                                                                              }
  2584. { MatchFileMask                                                                }
  2585. {                                                                              }
  2586. Function MatchFileMask (const Mask, Key : String; const CaseSensitive : Boolean) : Boolean;
  2587. var ML, KL : Integer;
  2588.  
  2589.   Function MatchAt (MaskPos, KeyPos : Integer) : Boolean;
  2590.     Begin
  2591.       While (MaskPos <= ML) and (KeyPos <= KL) do
  2592.         Case Mask [MaskPos] of
  2593.           '?' :
  2594.             begin
  2595.               Inc (MaskPos);
  2596.               Inc (KeyPos);
  2597.             end;
  2598.           '*' :
  2599.             begin
  2600.               While (MaskPos <= ML) and (Mask [MaskPos] = '*') do
  2601.                 Inc (MaskPos);
  2602.               if MaskPos > ML then
  2603.                 begin
  2604.                   Result := True;
  2605.                   exit;
  2606.                 end;
  2607.               Repeat
  2608.                 if MatchAt (MaskPos, KeyPos) then
  2609.                   begin
  2610.                     Result := True;
  2611.                     exit;
  2612.                   end;
  2613.                 Inc (KeyPos);
  2614.               Until KeyPos > KL;
  2615.               Result := False;
  2616.               exit;
  2617.             end;
  2618.           else
  2619.             if not Match (Mask [MaskPos], Key [KeyPos], CaseSensitive) then
  2620.               begin
  2621.                 Result := False;
  2622.                 exit;
  2623.               end else
  2624.               begin
  2625.                 Inc (MaskPos);
  2626.                 Inc (KeyPos);
  2627.               end;
  2628.         end;
  2629.       While (MaskPos <= ML) and (Mask [MaskPos] in ['?', '*']) do
  2630.         Inc (MaskPos);
  2631.       if (MaskPos <= ML) or (KeyPos <= KL) then
  2632.         begin
  2633.           Result := False;
  2634.           exit;
  2635.         end;
  2636.       Result := True;
  2637.     End;
  2638.  
  2639.   Begin
  2640.     ML := Length (Mask);
  2641.     if ML = 0 then
  2642.       begin
  2643.         Result := True;
  2644.         exit;
  2645.       end;
  2646.     KL := Length (Key);
  2647.     Result := MatchAt (1, 1);
  2648.   End;
  2649.  
  2650.  
  2651.  
  2652. {                                                                              }
  2653. { Format testing                                                               }
  2654. {                                                                              }
  2655. Function MatchNumber (const S : String; const Index : Integer = 1) : Integer;
  2656.   Begin
  2657.     Result := MatchCount (cs_Numeric, S, Index);
  2658.   End;
  2659.  
  2660. Function MatchHexNumber (const S : String; const Index : Integer = 1) : Integer;
  2661.   Begin
  2662.     Result := MatchCount (cs_HexDigit, S, Index);
  2663.   End;
  2664.  
  2665. Function MatchInteger (const S : String; const Index : Integer = 1) : Integer;
  2666.   Begin
  2667.     MatchQuantSeq (Result, [cs_Sign, cs_Numeric], [mqOptional, mqLeastOnce], S, [moDeterministic], Index);
  2668.     if Result > 0 then
  2669.       Dec (Result, Index - 1);
  2670.   End;
  2671.  
  2672. Function MatchReal (const S : String; const Index : Integer = 1) : Integer;
  2673. var I, J : Integer;
  2674.   Begin
  2675.     I := MatchInteger (S, Index);
  2676.     MatchQuantSeq (J, [['.'], cs_Numeric], [mqOnce, mqLeastOnce], S, [moDeterministic], Index + I);
  2677.     if J > 0 then
  2678.       Dec (J, Index + I - 1);
  2679.     Result := I + J;
  2680.   End;
  2681.  
  2682. Function MatchSciReal (const S : String; const Index : Integer = 1) : Integer;
  2683. var I : Integer;
  2684.   Begin
  2685.     Result := MatchReal (S, Index);
  2686.     if (Result = 0) or (Result + Index > Length (S)) then
  2687.       exit;
  2688.     MatchQuantSeq (I, [['E', 'e'], cs_Sign, cs_Numeric], [mqOnce, mqOptional, mqLeastOnce], S, [moDeterministic], Index + Result);
  2689.     if I > 0 then
  2690.       Inc (Result, I - (Index + Result - 1));
  2691.   End;
  2692.  
  2693. Function MatchQuotedString (const S : String; const ValidQuotes : CharSet; const Index : Integer) : Integer;
  2694. var Quote : Char;
  2695.     I, L  : Integer;
  2696.     R     : Boolean;
  2697.   Begin
  2698.     L := Length (S);
  2699.     if (Index < 1) or (L < Index + 1) or not (S [Index] in ValidQuotes) then
  2700.       begin
  2701.         Result := 0;
  2702.         exit;
  2703.       end;
  2704.     Quote := S [Index];
  2705.     I := Index + 1;
  2706.     R := False;
  2707.     Repeat
  2708.       I := Pos (Quote, S, [], I);
  2709.       if I = 0 then // no closing quote
  2710.         begin
  2711.           Result := 0;
  2712.           exit;
  2713.         end else
  2714.         if I = L then // closing quote is last character
  2715.           R := True else
  2716.           if S [I + 1] <> Quote then // not double quoted
  2717.             R := True else
  2718.             Inc (I, 2);
  2719.     Until R;
  2720.     Result := I - Index + 1;
  2721.   End;
  2722.  
  2723. Function IsNumber (const S : String) : Boolean;
  2724. var L : Integer;
  2725.   Begin
  2726.     L := Length (S);
  2727.     Result := (L > 0) and (MatchNumber (S) = L);
  2728.   End;
  2729.  
  2730. Function IsHexNumber (const S : String) : Boolean;
  2731. var L : Integer;
  2732.   Begin
  2733.     L := Length (S);
  2734.     Result := (L > 0) and (MatchHexNumber (S) = L);
  2735.   End;
  2736.  
  2737. Function IsInteger (const S : String) : Boolean;
  2738. var L : Integer;
  2739.   Begin
  2740.     L := Length (S);
  2741.     Result := (L > 0) and (MatchInteger (S) = L);
  2742.   End;
  2743.  
  2744. Function IsReal (const S : String) : Boolean;
  2745. var L : Integer;
  2746.   Begin
  2747.     L := Length (S);
  2748.     Result := (L > 0) and (MatchReal (S) = L);
  2749.   End;
  2750.  
  2751. Function IsSciReal (const S : String) : Boolean;
  2752. var L : Integer;
  2753.   Begin
  2754.     L := Length (S);
  2755.     Result := (L > 0) and (MatchSciReal (S) = L);
  2756.   End;
  2757.  
  2758. Function IsQuotedString (const S : String; const ValidQuotes : CharSet) : Boolean;
  2759. var L : Integer;
  2760.   Begin
  2761.     L := Length (S);
  2762.     if (L < 2) or (S [1] <> S [L]) or not (S [1] in ValidQuotes) then
  2763.       Result := False else
  2764.       Result := MatchQuotedString (S, ValidQuotes) = L;
  2765.   End;
  2766.  
  2767. Function IsHexDigit (const C : Char) : Boolean;
  2768.   Begin
  2769.     Result := C in ['0'..'9', 'A'..'F', 'a'..'f'];
  2770.   End;
  2771.  
  2772. Function HexDigitValue (const C : Char) : Byte;
  2773.   Begin
  2774.     Case C of
  2775.       '0'..'9' : Result := Byte (C) - Byte ('0');
  2776.       'A'..'F' : Result := Byte (C) - Byte ('A') + 10;
  2777.       'a'..'f' : Result := Byte (C) - Byte ('a') + 10;
  2778.     else
  2779.       raise EConvertError.Create ('Not a valid hex digit');
  2780.     end;
  2781.   End;
  2782.  
  2783.  
  2784.  
  2785. {                                                                              }
  2786. { Trim                                                                         }
  2787. {                                                                              }
  2788. Function TrimLeft (const S : String; const TrimSet : CharSet) : String;
  2789. var F, L : Integer;
  2790.   Begin
  2791.     L := Length (S);
  2792.     F := 1;
  2793.     While (F <= L) and (S [F] in TrimSet) do
  2794.       Inc (F);
  2795.     Result := CopyFrom (S, F);
  2796.   End;
  2797.  
  2798. Procedure TrimLeftInPlace (var S : String; const TrimSet : CharSet);
  2799. var F, L : Integer;
  2800.     P : PChar;
  2801.   Begin
  2802.     L := Length (S);
  2803.     F := 1;
  2804.     While (F <= L) and (S [F] in TrimSet) do
  2805.       Inc (F);
  2806.     if F > L then
  2807.       S := '' else
  2808.       if F > 1 then
  2809.         begin
  2810.           L := L - F + 1;
  2811.           if L > 0 then
  2812.             begin
  2813.               P := Pointer (S);
  2814.               Inc (P, F - 1);
  2815.               MoveMem (P^, Pointer (S)^, L);
  2816.             end;
  2817.           SetLength (S, L);
  2818.         end;
  2819.   End;
  2820.  
  2821. Function TrimLeftStr (const S : String; const TrimStr : String; const CaseSensitive : Boolean) : String;
  2822. var F, L, M : Integer;
  2823.   Begin
  2824.     L := Length (TrimStr);
  2825.     M := Length (S);
  2826.     F := 1;
  2827.     While (F <= M) and Match (TrimStr, S, F, CaseSensitive) do
  2828.       Inc (F, L);
  2829.     Result := CopyFrom (S, F);
  2830.   End;
  2831.  
  2832. Function TrimRight (const S : String; const TrimSet : CharSet) : String;
  2833. var F : Integer;
  2834.   Begin
  2835.     F := Length (S);
  2836.     While (F >= 1) and (S [F] in TrimSet) do
  2837.       Dec (F);
  2838.     Result := CopyLeft (S, F);
  2839.   End;
  2840.  
  2841. Procedure TrimRightInPlace (var S : String; const TrimSet : CharSet);
  2842. var F : Integer;
  2843.   Begin
  2844.     F := Length (S);
  2845.     While (F >= 1) and (S [F] in TrimSet) do
  2846.       Dec (F);
  2847.     if F = 0 then
  2848.       S := '' else
  2849.       SetLength (S, F);
  2850.   End;
  2851.  
  2852. Function TrimRightStr (const S : String; const TrimStr : String; const CaseSensitive : Boolean) : String;
  2853. var F, L : Integer;
  2854.   Begin
  2855.     L := Length (TrimStr);
  2856.     F := Length (S) - L  + 1;
  2857.     While (F >= 1) and Match (TrimStr, S, F, CaseSensitive) do
  2858.       Dec (F, L);
  2859.     Result := CopyLeft (S, F + L - 1);
  2860.   End;
  2861.  
  2862. Function Trim (const S : String; const TrimSet : CharSet) : String;
  2863. var F, G, L : Integer;
  2864.   Begin
  2865.     L := Length (S);
  2866.     F := 1;
  2867.     While (F <= L) and (S [F] in TrimSet) do
  2868.       Inc (F);
  2869.     G := L;
  2870.     While (G >= F) and (S [G] in TrimSet) do
  2871.       Dec (G);
  2872.     Result := CopyRange (S, F, G);
  2873.   End;
  2874.  
  2875. Procedure TrimInPlace (var S : String; const TrimSet : CharSet);
  2876.   Begin
  2877.     TrimLeftInPlace (S, TrimSet);
  2878.     TrimRightInPlace (S, TrimSet);
  2879.   End;
  2880.  
  2881. Function TrimStr (const S : String; const TrimStr : String; const CaseSensitive : Boolean) : String;
  2882. var F, G, L : Integer;
  2883.   Begin
  2884.     L := Length (S);
  2885.     F := 1;
  2886.     While (F <= L) and Match (TrimStr, S, F, CaseSensitive) do
  2887.       Inc (F);
  2888.     G := L;
  2889.     While (G >= F) and Match (TrimStr, S, G, CaseSensitive) do
  2890.       Dec (G);
  2891.     Result := CopyRange (S, F, G);
  2892.   End;
  2893.  
  2894. Procedure Trim (var S : StringArray; const TrimSet : CharSet);
  2895. var I : Integer;
  2896.   Begin
  2897.     For I := 0 to Length (S) - 1 do
  2898.       TrimInPlace (S [I], TrimSet);
  2899.   End;
  2900.  
  2901. Procedure TrimStr (var S : StringArray; const TrimStr : String; const CaseSensitive : Boolean);
  2902. var I : Integer;
  2903.   Begin
  2904.     For I := 0 to Length (S) - 1 do
  2905.       S [I] := cStrings.TrimStr (S [I], TrimStr, CaseSensitive);
  2906.   End;
  2907.  
  2908. Function TrimEllipse (const S : String; const Length : Integer) : String;
  2909.   Begin
  2910.     if System.Length (S) <= Length then
  2911.       Result := S else
  2912.       if Length < 3 then
  2913.         Result := Dup ('.', Length) else
  2914.         Result := CopyLeft (S, Length - 3) + '...';
  2915.   End;
  2916.  
  2917. Function TrimQuotes (const S : String; const Quotes : CharSet) : String;
  2918. var L : Integer;
  2919.   Begin
  2920.     L := Length (S);
  2921.     if (L >= 2) and (S [1] = S [L]) and (S [1] in Quotes) then
  2922.       Result := Copy (S, 2, L - 2) else
  2923.       Result := S;
  2924.   End;
  2925.  
  2926. {                                                                              }
  2927. { Pad                                                                          }
  2928. {                                                                              }
  2929. Function PadLeft (const S : String; const PadChar : Char; const Length : Integer;
  2930.          const Cut : Boolean = False) : String;
  2931. var F, L, P, M : Integer;
  2932.     I, J : PChar;
  2933.   Begin
  2934.     if Length = 0 then
  2935.       begin
  2936.         if Cut then
  2937.           Result := '' else
  2938.           Result := S;
  2939.         exit;
  2940.       end;
  2941.  
  2942.     M := System.Length (S);
  2943.     if Length = M then
  2944.       begin
  2945.         Result := S;
  2946.         exit;
  2947.       end;
  2948.  
  2949.     if Cut then
  2950.       L := Length else
  2951.       L := MaxI (Length, M);
  2952.     P := MaxI (0, L - M);
  2953.  
  2954.     SetLength (Result, L);
  2955.     if P > 0 then
  2956.       FillChar (Pointer (Result)^, P, PadChar);
  2957.     if L > P then
  2958.       begin
  2959.         I := Pointer (Result);
  2960.         J := Pointer (S);
  2961.         Inc (I, P);
  2962.         For F := 1 to L - P do
  2963.           begin
  2964.             I^ := J^;
  2965.             Inc (I);
  2966.             Inc (J);
  2967.           end;
  2968.       end;
  2969.   End;
  2970.  
  2971. Function PadRight (const S : String; const PadChar : Char; const Length : Integer;
  2972.          const Cut : Boolean = False) : String;
  2973. var F, L, P, M : Integer;
  2974.     I, J : PChar;
  2975.   Begin
  2976.     if Length = 0 then
  2977.       begin
  2978.         if Cut then
  2979.           Result := '' else
  2980.           Result := S;
  2981.         exit;
  2982.       end;
  2983.  
  2984.     M := System.Length (S);
  2985.     if Length = M then
  2986.       begin
  2987.         Result := S;
  2988.         exit;
  2989.       end;
  2990.  
  2991.     if Cut then
  2992.       L := Length else
  2993.       L := MaxI (Length, M);
  2994.     P := MaxI (0, L - M);
  2995.  
  2996.     SetLength (Result, L);
  2997.     if L > P then
  2998.       begin
  2999.         I := Pointer (Result);
  3000.         J := Pointer (S);
  3001.         For F := 1 to L - P do
  3002.           begin
  3003.             I^ := J^;
  3004.             Inc (I);
  3005.             Inc (J);
  3006.           end;
  3007.       end;
  3008.     if P > 0 then
  3009.       FillChar (Result [L - P + 1], P, PadChar);
  3010.   End;
  3011.  
  3012. Function Pad (const S : String; const PadChar : Char; const Length : Integer;
  3013.          const Cut : Boolean) : String;
  3014. var I : Integer;
  3015.   Begin
  3016.     I := Length - System.Length (S);
  3017.     Result := Dup (PadChar, I div 2) + S + Dup (PadChar, (I + 1) div 2);
  3018.     if Cut then
  3019.       SetLength (Result, Length);
  3020.   End;
  3021.  
  3022. Function Pad (const I : Integer; const Length : Integer; const Cut : Boolean) : String;
  3023.   Begin
  3024.     Result := PadLeft (IntToStr (I), '0', Length, Cut);
  3025.   End;
  3026.  
  3027. {$WARNINGS OFF}
  3028. Function PadInside (const S : String; const PadChar : Char; const Length : Integer) : String;
  3029. var I, J, K, C, M : Integer;
  3030.     P : CharSetArray;
  3031.   Begin
  3032.     if System.Length (S) >= Length then
  3033.       begin
  3034.         Result := S;
  3035.         exit;
  3036.       end;
  3037.  
  3038.     P := AsCharSetArray ([[PadChar], cs_AllChars - [PadChar]]);
  3039.     I := CountSeq (P, S);
  3040.     if I = 0 then // nowhere to pad inside
  3041.       begin
  3042.         Result := S;
  3043.         exit;
  3044.       end;
  3045.  
  3046.     C := (Length - System.Length (S)) div I;
  3047.     M := (Length - System.Length (S)) mod I;
  3048.     I := PosSeq (P, S);
  3049.     K := 0;
  3050.     Result := CopyLeft (S, I);
  3051.     Repeat
  3052.       Result := Result + Dup (PadChar, C);
  3053.       if K < M then
  3054.         Result := Result + PadChar;
  3055.       Inc (K);
  3056.       J := I;
  3057.       I := PosSeq (P, S, [], J + 1);
  3058.       if I > 0 then
  3059.         Result := Result + CopyRange (S, J + 1, I);
  3060.     Until I = 0;
  3061.     Result := Result + CopyFrom (S, J + 1);
  3062.   End;
  3063. {$WARNINGS ON}
  3064.  
  3065. Function IntToPadStr (const I : Integer; const PadType : TPadType; const Len : Integer) : String;
  3066. var J : Integer;
  3067.     N : Boolean;
  3068.   Begin
  3069.     J := I;
  3070.     N := J < 0;
  3071.     if N then
  3072.       J := -J;
  3073.     Result := LongWordToStr (LongWord (J));
  3074.     if PadType = padLeftZero then
  3075.       Result := PadLeft (Result, '0', Len, False);
  3076.     if N then
  3077.       Result := '-' + Result;
  3078.     Case PadType of
  3079.       padLeftSpace  : Result := PadLeft (Result, ' ', Len, False);
  3080.       padRightSpace : Result := PadRight (Result, ' ', Len, False);
  3081.     end;
  3082.   End;
  3083.  
  3084.  
  3085. { TranslateStartStop translates Start, Stop parameters (negative values are    }
  3086. { indexed from back of string) into StartIdx and StopIdx (relative to start).  }
  3087. { Returns False if the Start, Stop does not specify a valid range.             }
  3088. Function TranslateStart (const S : String; const Start : Integer; var Len, StartIndex : Integer) : Boolean;
  3089.   Begin
  3090.     Len := Length (S);
  3091.     if Len = 0 then
  3092.       Result := False else
  3093.       begin
  3094.         StartIndex := Start;
  3095.         if Start < 0 then
  3096.           Inc (StartIndex, Len + 1);
  3097.         if StartIndex > Len then
  3098.           Result := False else
  3099.           begin
  3100.             if StartIndex < 1 then
  3101.               StartIndex := 1;
  3102.             Result := True;
  3103.           end;
  3104.       end;
  3105.   End;
  3106.  
  3107. Function TranslateStartStop (const S : String; const Start, Stop : Integer; var Len, StartIndex, StopIndex : Integer) : Boolean;
  3108.   Begin
  3109.     Len := Length (S);
  3110.     if Len = 0 then
  3111.       Result := False else
  3112.       begin
  3113.         StartIndex := Start;
  3114.         if Start < 0 then
  3115.           Inc (StartIndex, Len + 1);
  3116.         StopIndex := Stop;
  3117.         if StopIndex < 0 then
  3118.           Inc (StopIndex, Len + 1);
  3119.         if (StopIndex < 1) or (StartIndex > Len) or (StopIndex < StartIndex) then
  3120.           Result := False else
  3121.           begin
  3122.             if StopIndex > Len then
  3123.               StopIndex:= Len;
  3124.             if StartIndex < 1 then
  3125.               StartIndex := 1;
  3126.             Result := True;
  3127.           end;
  3128.       end;
  3129.   End;
  3130.  
  3131.  
  3132.  
  3133. {                                                                              }
  3134. { Paste                                                                        }
  3135. {                                                                              }
  3136. Function Paste (const Source : String; var Dest : String; var DestIndex : Integer; const ReverseDirection : Boolean; const SourceStart : Integer; const SourceStop : Integer) : Integer;
  3137. var SI, SJ, SL, DI, DL : Integer;
  3138.   Begin
  3139.     Result := -1;
  3140.     if not TranslateStartStop (Source, SourceStart, SourceStop, SL, SI, SJ) then
  3141.       exit;
  3142.     if not TranslateStart (Dest, DestIndex, DL, DI) then
  3143.       exit;
  3144.     if ReverseDirection then
  3145.       DI := MaxI (DI - (SJ - SI), 1);
  3146.     Result := MinI (SJ - SI + 1, DL - DI + 1);
  3147.     if Result > 0 then
  3148.       begin
  3149.         MoveMem (Source [SI], Dest [DI], Result);
  3150.         if ReverseDirection then
  3151.           DestIndex := DI - 1 else
  3152.           DestIndex := DI + Result;
  3153.       end;
  3154.   End;
  3155.  
  3156.  
  3157.  
  3158. {                                                                              }
  3159. { CopyEx                                                                       }
  3160. {                                                                              }
  3161. Function CopyEx (const S : String; const Start, Count : Integer) : String;
  3162. var I, L : Integer;
  3163.   Begin
  3164.     if (Count < 0) or not TranslateStart (S, Start, L, I) then
  3165.       Result := '' else
  3166.       if (I = 1) and (Count >= L) then
  3167.         Result := S else
  3168.         Result := Copy (S, I, Count);
  3169.   End;
  3170.  
  3171. Function CopyRangeEx (const S : String; const Start, Stop : Integer) : String;
  3172. var I, J, L : Integer;
  3173.   Begin
  3174.     if not TranslateStartStop (S, Start, Stop, L, I, J) then
  3175.       Result := '' else
  3176.       if (I = 1) and (J = L) then
  3177.         Result := S else
  3178.         Result := Copy (S, I, J - I + 1);
  3179.   End;
  3180.  
  3181. Function CopyFromEx (const S : String; const Start : Integer) : String;
  3182. var I, L : Integer;
  3183.   Begin
  3184.     if not TranslateStart (S, Start, L, I) then
  3185.       Result := '' else
  3186.       if I <= 1 then
  3187.         Result := S else
  3188.         Result := Copy (S, I, L - I + 1);
  3189.   End;
  3190.  
  3191.  
  3192.  
  3193. {                                                                              }
  3194. { Find Options                                                                 }
  3195. {                                                                              }
  3196. Function FindOptions (const Reverse : Boolean; const CaseInsensitive : Boolean; const Overlapping : Boolean; const NonMatch : Boolean) : TFindOptions;
  3197.   Begin
  3198.     Result := [];
  3199.     if Reverse then
  3200.       Include (Result, foReverse);
  3201.     if CaseInsensitive then
  3202.       Include (Result, foCaseInsensitive);
  3203.     if Overlapping then
  3204.       Include (Result, foOverlapping);
  3205.     if NonMatch then
  3206.       Include (Result, foNonMatch);
  3207.   End;
  3208.  
  3209.  
  3210.  
  3211. {                                                                              }
  3212. { Pos                                                                          }
  3213. {                                                                              }
  3214.  
  3215. {$IFDEF WINTEL}
  3216. { Q_PosStr by Andrew N. Driazgov (andrey@asp.tstu.ru)                          }
  3217. { Optimized version of the general Pos case.                                   }
  3218. Function Q_PosStr (const Find, S : String; const StartIndex : Integer) : Integer;
  3219.   Asm
  3220.         PUSH    ESI
  3221.         PUSH    EDI
  3222.         PUSH    EBX
  3223.         PUSH    EDX
  3224.         TEST    EAX, EAX
  3225.         JE      @@qt
  3226.         TEST    EDX, EDX
  3227.         JE      @@qt0
  3228.         MOV     ESI, EAX
  3229.         MOV     EDI, EDX
  3230.         MOV     EAX, [EAX - 4]
  3231.         MOV     EDX, [EDX - 4]
  3232.         DEC     EAX
  3233.         SUB     EDX, EAX
  3234.         DEC     ECX
  3235.         SUB     EDX, ECX
  3236.         JNG     @@qt0
  3237.         XCHG    EAX, EDX
  3238.         ADD     EDI, ECX
  3239.         MOV     ECX, EAX
  3240.         JMP     @@nx
  3241. @@fr:   INC     EDI
  3242.         DEC     ECX
  3243.         JE      @@qt0
  3244. @@nx:   MOV     EBX,EDX
  3245.         MOV     AL, BYTE PTR [ESI]
  3246. @@lp1:  CMP     AL, BYTE PTR [EDI]
  3247.         JE      @@uu
  3248.         INC     EDI
  3249.         DEC     ECX
  3250.         JE      @@qt0
  3251.         CMP     AL, BYTE PTR [EDI]
  3252.         JE      @@uu
  3253.         INC     EDI
  3254.         DEC     ECX
  3255.         JE      @@qt0
  3256.         CMP     AL, BYTE PTR [EDI]
  3257.         JE      @@uu
  3258.         INC     EDI
  3259.         DEC     ECX
  3260.         JE      @@qt0
  3261.         CMP     AL, BYTE PTR [EDI]
  3262.         JE      @@uu
  3263.         INC     EDI
  3264.         DEC     ECX
  3265.         JNE     @@lp1
  3266. @@qt0:  XOR     EAX, EAX
  3267. @@qt:   POP     ECX
  3268.         POP     EBX
  3269.         POP     EDI
  3270.         POP     ESI
  3271.         RET
  3272. @@uu:   TEST    EDX, EDX
  3273.         JE      @@fd
  3274. @@lp2:  MOV     AL, BYTE PTR [ESI + EBX]
  3275.         CMP     AL, BYTE PTR [EDI + EBX]
  3276.         JNE     @@fr
  3277.         DEC     EBX
  3278.         JE      @@fd
  3279.         MOV     AL, BYTE PTR [ESI + EBX]
  3280.         CMP     AL, BYTE PTR [EDI + EBX]
  3281.         JNE     @@fr
  3282.         DEC     EBX
  3283.         JE      @@fd
  3284.         MOV     AL, BYTE PTR [ESI + EBX]
  3285.         CMP     AL, BYTE PTR [EDI + EBX]
  3286.         JNE     @@fr
  3287.         DEC     EBX
  3288.         JE      @@fd
  3289.         MOV     AL, BYTE PTR [ESI + EBX]
  3290.         CMP     AL, BYTE PTR [EDI + EBX]
  3291.         JNE     @@fr
  3292.         DEC     EBX
  3293.         JNE     @@lp2
  3294. @@fd:   LEA     EAX, [EDI + 1]
  3295.         SUB     EAX, [ESP]
  3296.         POP     ECX
  3297.         POP     EBX
  3298.         POP     EDI
  3299.         POP     ESI
  3300.   End;
  3301. {$ENDIF}
  3302.  
  3303. Function PosBuf (const Find : String; const Buf; const BufSize : Integer; const CaseSensitive : Boolean) : Integer;
  3304. var I : Integer;
  3305.     P : PChar;
  3306.   Begin
  3307.     if Find = '' then
  3308.       begin
  3309.         Result := -1;
  3310.         exit;
  3311.       end;
  3312.     P := @Buf;
  3313.     I := BufSize;
  3314.     While I > 0 do
  3315.       if MatchBuf (Find, P^, I, CaseSensitive) then
  3316.         begin
  3317.           Result := BufSize - I;
  3318.           exit;
  3319.         end else
  3320.         begin
  3321.           Inc (P);
  3322.           Dec (I);
  3323.         end;
  3324.     Result := -1;
  3325.   End;
  3326.  
  3327. Function Pos (const Find : String; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer) : Integer;
  3328. var C, F, I, J, L, G : Integer;
  3329.     CaseSensitive, FindNonMatch, Overlapping : Boolean;
  3330.   Begin
  3331.     C := Length (Find);
  3332.     if (C > 0) and TranslateStartStop (S, Start, Stop, L, I, J) then
  3333.       begin
  3334.         {$IFDEF WINTEL}
  3335.         if (J = L) and (Options = []) then // Optimzation for standard case
  3336.           begin
  3337.             Result := Q_PosStr (Find, S, I);
  3338.             exit;
  3339.           end;
  3340.         {$ENDIF}
  3341.  
  3342.         CaseSensitive := not (foCaseInsensitive in Options);
  3343.         FindNonMatch := foNonMatch in Options;
  3344.         Overlapping := foOverlapping in Options;
  3345.  
  3346.         if foReverse in Options then
  3347.           begin
  3348.             F := J - C + 1;
  3349.             While F >= I do
  3350.               if Match (Find, S, F, CaseSensitive) xor FindNonMatch then
  3351.                 begin
  3352.                   Result := F;
  3353.                   exit;
  3354.                 end else
  3355.                 if FindNonMatch and not Overlapping then
  3356.                   Dec (F, C) else
  3357.                   Dec (F);
  3358.           end else
  3359.           begin
  3360.             F := I;
  3361.             G := J - C + 1;
  3362.             While F <= G do
  3363.               if Match (Find, S, F, CaseSensitive) xor FindNonMatch then
  3364.                 begin
  3365.                   Result := F;
  3366.                   exit;
  3367.                 end else
  3368.                 if FindNonMatch and not Overlapping then
  3369.                   Inc (F, C) else
  3370.                   Inc (F);
  3371.           end;
  3372.       end;
  3373.     Result := 0;
  3374.   End;
  3375.  
  3376. Function Pos (const Find : Char; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer) : Integer;
  3377. var F, I, J, L : Integer;
  3378.     FindNonMatch : Boolean;
  3379.     P : PChar;
  3380.   Begin
  3381.     if TranslateStartStop (S, Start, Stop, L, I, J) then
  3382.       begin
  3383.         FindNonMatch := foNonMatch in Options;
  3384.  
  3385.         if foReverse in Options then
  3386.           begin
  3387.             if foCaseInsensitive in Options then
  3388.               begin
  3389.                 P := Pointer (S);
  3390.                 Inc (P, J - 1);
  3391.                 For F := J downto I do
  3392.                   if MatchNoCase (P^, Find) xor FindNonMatch then
  3393.                     begin
  3394.                       Result := F;
  3395.                       exit;
  3396.                     end else
  3397.                     Dec (P);
  3398.               end else
  3399.               begin
  3400.                 P := Pointer (S);
  3401.                 Inc (P, J - 1);
  3402.                 For F := J downto I do
  3403.                   if (P^ = Find) xor FindNonMatch then
  3404.                     begin
  3405.                       Result := F;
  3406.                       exit;
  3407.                     end else
  3408.                     Dec (P);
  3409.               end;
  3410.           end else
  3411.           if foCaseInsensitive in Options then
  3412.             begin
  3413.               P := Pointer (S);
  3414.               Inc (P, I - 1);
  3415.               For F := I to J do
  3416.                 if MatchNoCase (P^, Find) xor FindNonMatch then
  3417.                   begin
  3418.                     Result := F;
  3419.                     exit;
  3420.                   end else
  3421.                   Inc (P);
  3422.             end else
  3423.             begin
  3424.               P := Pointer (S);
  3425.               Inc (P, I - 1);
  3426.               For F := I to J do
  3427.                 if (P^ = Find) xor FindNonMatch then
  3428.                   begin
  3429.                     Result := F;
  3430.                     exit;
  3431.                   end else
  3432.                   Inc (P);
  3433.             end;
  3434.       end;
  3435.     Result := 0;
  3436.   End;
  3437.  
  3438. Function PosNext (const Find : String; const S : String; const LastPos : Integer; const Options : TFindOptions; const Start : Integer; const Stop : Integer) : Integer;
  3439. var C, L, I, J : Integer;
  3440.   Begin
  3441.     C := Length (Find);
  3442.     if (C > 0) and TranslateStartStop (S, Start, Stop, L, I, J) then
  3443.       begin
  3444.         if LastPos > 0 then
  3445.           if foReverse in Options then
  3446.             J := MinI (J, LastPos - C) else
  3447.             I := MaxI (I, LastPos + C);
  3448.         Result := Pos (Find, S, Options, I, J);
  3449.       end else
  3450.       Result := 0;
  3451.   End;
  3452.  
  3453. Function PosNextSeq (const Find : Array of CharSet; const S : String; const LastPos : Integer; const Options : TFindOptions; const Start : Integer; const Stop : Integer) : Integer;
  3454. var C, L, I, J : Integer;
  3455.   Begin
  3456.     C := Length (Find);
  3457.     if (C > 0) and TranslateStartStop (S, Start, Stop, L, I, J) then
  3458.       begin
  3459.         if LastPos > 0 then
  3460.           if foReverse in Options then
  3461.             J := MinI (J, LastPos - C) else
  3462.             I := MaxI (I, LastPos + C);
  3463.         Result := PosSeq (Find, S, Options, I, J);
  3464.       end else
  3465.       Result := 0;
  3466.   End;
  3467.  
  3468. Function PosNext (const Find : Char; const S : String; const LastPos : Integer; const Options : TFindOptions; const Start : Integer; const Stop : Integer) : Integer;
  3469. var L, I, J : Integer;
  3470.   Begin
  3471.     if TranslateStartStop (S, Start, Stop, L, I, J) then
  3472.       begin
  3473.         if LastPos > 0 then
  3474.           if foReverse in Options then
  3475.             J := MinI (J, LastPos - 1) else
  3476.             I := MaxI (I, LastPos + 1);
  3477.         Result := Pos (Find, S, Options, I, J);
  3478.       end else
  3479.       Result := 0;
  3480.   End;
  3481.  
  3482. Function PosNext (const Find : CharSet; const S : String; const LastPos : Integer; const Options : TFindOptions; const Start : Integer; const Stop : Integer) : Integer;
  3483. var L, I, J : Integer;
  3484.   Begin
  3485.     if TranslateStartStop (S, Start, Stop, L, I, J) then
  3486.       begin
  3487.         if LastPos > 0 then
  3488.           if foReverse in Options then
  3489.             J := MinI (J, LastPos - 1) else
  3490.             I := MaxI (I, LastPos + 1);
  3491.         Result := Pos (Find, S, Options, I, J);
  3492.       end else
  3493.       Result := 0;
  3494.   End;
  3495.  
  3496. Function PosStrings (const Find : Array of String; const S : String; var FindItem : Integer; const Options : TFindOptions; const Start : Integer; const Stop : Integer) : Integer;
  3497. var F, I, J, L : Integer;
  3498.     CaseSensitive, FindNonMatch, Overlapping : Boolean;
  3499.   Begin
  3500.     if (High (Find) >= 0) and TranslateStartStop (S, Start, Stop, L, I, J) then
  3501.       begin
  3502.         CaseSensitive := not (foCaseInsensitive in Options);
  3503.         FindNonMatch := foNonMatch in Options;
  3504.         Overlapping := foOverlapping in Options;
  3505.  
  3506.         if foReverse in Options then
  3507.           begin
  3508.             F := J;
  3509.             While F >= I do
  3510.               begin
  3511.                 FindItem := MatchStrings (S, Find, CaseSensitive, F, J - F + 1);
  3512.                 if (FindItem >= 0) xor FindNonMatch then
  3513.                   begin
  3514.                     Result := F;
  3515.                     exit;
  3516.                   end else
  3517.                   if FindNonMatch and not Overlapping then
  3518.                     Dec (F, Length (Find [FindItem])) else
  3519.                     Dec (F);
  3520.               end;
  3521.           end else
  3522.           begin
  3523.             F := I;
  3524.             While F <= J do
  3525.               begin
  3526.                 FindItem := MatchStrings (S, Find, CaseSensitive, F, J - F + 1);
  3527.                 if (FindItem >= 0) xor FindNonMatch then
  3528.                   begin
  3529.                     Result := F;
  3530.                     exit;
  3531.                   end else
  3532.                   if FindNonMatch and not Overlapping then
  3533.                     Inc (F, Length (Find [FindItem])) else
  3534.                     Inc (F);
  3535.               end;
  3536.           end;
  3537.       end;
  3538.     Result := 0;
  3539.   End;
  3540.  
  3541. Function PosSeq (const Find : Array of CharSet; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer) : Integer;
  3542. var C, F, I, J, L, G : Integer;
  3543.     CaseSensitive, FindNonMatch, Overlapping : Boolean;
  3544.   Begin
  3545.     C := Length (Find);
  3546.     if (C > 0) and TranslateStartStop (S, Start, Stop, L, I, J) then
  3547.       begin
  3548.         CaseSensitive := not (foCaseInsensitive in Options);
  3549.         FindNonMatch := foNonMatch in Options;
  3550.         Overlapping := foOverlapping in Options;
  3551.  
  3552.         if foReverse in Options then
  3553.           begin
  3554.             F := J - C + 1;
  3555.             While F >= I do
  3556.               if MatchSeq (Find, S, F, CaseSensitive) xor FindNonMatch then
  3557.                 begin
  3558.                   Result := F;
  3559.                   exit;
  3560.                 end else
  3561.                 if FindNonMatch and not Overlapping then
  3562.                   Dec (F, C) else
  3563.                   Dec (F);
  3564.           end else
  3565.           begin
  3566.             F := I;
  3567.             G := J - C + 1;
  3568.             While F <= G do
  3569.               if MatchSeq (Find, S, F, CaseSensitive) xor FindNonMatch then
  3570.                 begin
  3571.                   Result := F;
  3572.                   exit;
  3573.                 end else
  3574.                 if FindNonMatch and not Overlapping then
  3575.                   Inc (F, C) else
  3576.                   Inc (F);
  3577.           end;
  3578.       end;
  3579.     Result := 0;
  3580.   End;
  3581.  
  3582. Function PosChars (const Find : Array of Char; const S : String; var FindItem : Integer; const Options : TFindOptions; const Start : Integer; const Stop : Integer) : Integer;
  3583. var F, I, J, L : Integer;
  3584.     CaseSensitive, FindNonMatch : Boolean;
  3585.   Begin
  3586.     if (High (Find) >= 0) and TranslateStartStop (S, Start, Stop, L, I, J) then
  3587.       begin
  3588.         CaseSensitive := not (foCaseInsensitive in Options);
  3589.         FindNonMatch := foNonMatch in Options;
  3590.  
  3591.         if foReverse in Options then
  3592.           begin
  3593.             For F := J downto I do
  3594.               begin
  3595.                 FindItem := MatchChars (S [F], Find, CaseSensitive);
  3596.                 if (FindItem >= 0) xor FindNonMatch then
  3597.                   begin
  3598.                     Result := F;
  3599.                     exit;
  3600.                   end;
  3601.               end;
  3602.           end else
  3603.           For F := I to J do
  3604.               begin
  3605.                 FindItem := MatchChars (S [F], Find, CaseSensitive);
  3606.                 if (FindItem >= 0) xor FindNonMatch then
  3607.                   begin
  3608.                     Result := F;
  3609.                     exit;
  3610.                   end;
  3611.               end;
  3612.       end;
  3613.     Result := 0;
  3614.   End;
  3615.  
  3616. Function Pos (const Find : CharSet; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer) : Integer;
  3617. var F, I, J, L : Integer;
  3618.     CaseSensitive, FindNonMatch : Boolean;
  3619.   Begin
  3620.     if TranslateStartStop (S, Start, Stop, L, I, J) and (Find <> []) then
  3621.       begin
  3622.         CaseSensitive := not (foCaseInsensitive in Options);
  3623.         FindNonMatch := foNonMatch in Options;
  3624.  
  3625.         if foReverse in Options then
  3626.           begin
  3627.             For F := J downto I do
  3628.               if Match (Find, S [F], CaseSensitive) xor FindNonMatch then
  3629.                 begin
  3630.                   Result := F;
  3631.                   exit;
  3632.                 end;
  3633.           end else
  3634.           For F := I to J do
  3635.             if Match (Find, S [F], CaseSensitive) xor FindNonMatch then
  3636.               begin
  3637.                 Result := F;
  3638.                 exit;
  3639.               end;
  3640.       end;
  3641.     Result := 0;
  3642.   End;
  3643.  
  3644. { Boyer-Moore-Horspool pattern searching                                       }
  3645. { Converted to a class and rewritten in assembly by David Butler               }
  3646. {  (david@e.co.za) from a highly optimized Pascal unit BMH 1.11a written by    }
  3647. {  Jody R Cairns (jodyc@cs.mun.ca) as she took it from the 'Handbook of        }
  3648. {  Algorithms and Data Structures in Pascal and C', Second Edition,            }
  3649. {  by G.H Gonnet and  R. Baeza-Yates.                                          }
  3650. Constructor TBMHSearcher.Create (const Find : String);
  3651.   Begin
  3652.     inherited Create;
  3653.     FFind := Find;
  3654.  
  3655.     { Creates a Boyer-Moore-Horspool index table for the search string }
  3656.     asm
  3657.       push eax
  3658.       push ebx
  3659.       push edx
  3660.       push esi
  3661.       push edi                        // save state
  3662.  
  3663.       mov eax, self
  3664.       mov edx, offset FTable
  3665.       add edx, eax                    // edx = FTable [0]
  3666.       mov ebx, [eax + FFind]          // ebx = FFind [1]
  3667.       or ebx, ebx
  3668.       jz @exit
  3669.  
  3670.       // FTable [0..255] := Length (FFind)                                    //
  3671.       mov eax, [ebx - 4]              { eax = Length (FFind) }                //
  3672.       mov edi, edx                    { edi = FTable [0] }                    //
  3673.       mov ecx, 256                                                            //
  3674.       rep stosd                                                               //
  3675.  
  3676.       // FTable [FFind [i = 1..Length (FFind)]] = Length (FFind) - i          //
  3677.       mov ecx, eax                    { ecx = Length (FFind) }                //
  3678.       dec ecx                                                                 //
  3679.       mov edi, edx                    { edi = FTable [0] }                    //
  3680.       xor esi, esi                    { esi = i - 1 = 0 }                     //
  3681.       xor edx, edx                                                            //
  3682.     @c1:                                                                      //
  3683.       mov dl, [ebx + esi]             { edx = FFind [i] }                     //
  3684.       mov [edi + edx * 4], ecx        { FTable [edx] := Length (FFind) - i }  //
  3685.       inc esi                                                                 //
  3686.       loop @c1                                                                //
  3687.  
  3688.     @exit:
  3689.       pop edi                         // restore state
  3690.       pop esi
  3691.       pop edx
  3692.       pop ebx
  3693.       pop eax
  3694.     end;
  3695.   End;
  3696.  
  3697. Function TBMHSearcher.Pos (const S : String; const StartIndex : Integer; const StopIndex : Integer) : Integer;
  3698.   Asm
  3699.       push ebp
  3700.       push ebx
  3701.       push edx
  3702.       push esi
  3703.       push edi                                      // save state
  3704.  
  3705.       push StartIndex
  3706.       push S
  3707.       push self                                     // push parameters
  3708.       pop ebp                                       // ebp = self
  3709.       pop eax                                       // eax = S
  3710.       pop ebx                                       // ebx = StartIndex
  3711.  
  3712.       or eax, eax
  3713.       jz @NoMatch
  3714.  
  3715.       cmp ebx, 1
  3716.       jae @StartIndexValid
  3717.       mov ebx, 1
  3718.     @StartIndexValid:
  3719.  
  3720.       mov edx, [eax - 4]
  3721.       push StopIndex
  3722.       pop ecx
  3723.       cmp ecx, 1
  3724.       jae @StopIndexValid1
  3725.       mov ecx, edx
  3726.       jmp @StopIndexValid2
  3727.     @StopIndexValid1:
  3728.       cmp ecx, edx
  3729.       jbe @StopIndexValid2
  3730.       mov ecx, edx
  3731.     @StopIndexValid2:                               // ecx = Min (StopIndex, Length (S))
  3732.  
  3733.       mov ecx, [eax - 4]
  3734.       mov edx, [ebp + FFind]                        // edx = FFind
  3735.       or edx, edx
  3736.       jz @NoMatch                                   // if FFind = '' then NoMatch
  3737.       add ebx, [edx - 4]
  3738.       dec ebx                                       // ebx = counter, starting at StartIndex + Length (FFind) - 1
  3739.  
  3740.     // while ebx < Min (Length (s), StopIndex)                                                    //
  3741.     @WhileNotEnd:                                                                                 //
  3742.       cmp ebx, ecx                                                                                //
  3743.       ja @NoMatch                                                                                 //
  3744.       push ecx                                                                                    //
  3745.                                                                                                   //
  3746.       mov ecx, [edx - 4]                            { loop count = Length (FFind) }               //
  3747.                                                                                                   //
  3748.       mov esi, eax                                                                                //
  3749.       add esi, ebx                                                                                //
  3750.       sub esi, ecx                                  { esi = S [1 + ebx - Length (FFind)] }        //
  3751.                                                                                                   //
  3752.       mov edi, edx                                  { edi = FFind [1] }                           //
  3753.                                                                                                   //
  3754.     { This is actually faster than REP CMPSB on a Pentium                     {}                  //
  3755.     @c1:                                                                      {}                  //
  3756.       cmpsb                                                                   {}                  //
  3757.       jne @NotEq                                                              {}                  //
  3758.       loop @c1                                                                {}                  //
  3759.                                                                                                   //
  3760.       pop ecx                                                                                     //
  3761.       jmp @Match                                    { Match found }                               //
  3762.                                                                                                   //
  3763.     @NotEq:                                                                                       //
  3764.       xor ecx, ecx                                                                                //
  3765.       mov cl, [eax + ebx - 1]                       { ecx = S [ebx] }                             //
  3766.       add ebx, dword ptr [ebp + FTable + ecx * 4]   { Inc (ebx, FTable [ecx]) }                   //
  3767.                                                                                                   //
  3768.       pop ecx                                                                                     //
  3769.       jmp @WhileNotEnd                                                                            //
  3770.  
  3771.     @NoMatch:
  3772.       xor eax, eax                                  // Result = 0
  3773.       jmp @Fin
  3774.  
  3775.     @Match:
  3776.       mov eax, ebx
  3777.       inc eax
  3778.       sub eax, [edx - 4]                            // Result = ebx - Length (FFind) + 1
  3779.  
  3780.     @Fin:
  3781.       pop edi                                       // Restore state
  3782.       pop esi
  3783.       pop edx
  3784.       pop ebx
  3785.       pop ebp
  3786.   End;
  3787.  
  3788. Function PosBMH (const Find : String; const S : String; const StartIndex : Integer; const StopIndex : Integer) : Integer;
  3789. var B : TBMHSearcher;
  3790.   Begin
  3791.     B := TBMHSearcher.Create (Find);
  3792.     try
  3793.       Result := B.Pos (S, StartIndex, StopIndex);
  3794.     finally
  3795.       FreeAndNil (B);
  3796.     end;
  3797.   End;
  3798.  
  3799.  
  3800.  
  3801. {                                                                              }
  3802. { NextStartIndex                                                               }
  3803. {                                                                              }
  3804. Function NextStartIndex (const LastPos : Integer; const Options : TFindOptions; const FindLen : Integer) : Integer;
  3805.   Begin
  3806.     if foReverse in Options then
  3807.       begin
  3808.         if not (foOverlapping in Options) and not (foNonMatch in Options) then
  3809.           Result := LastPos - FindLen else
  3810.           Result := LastPos - 1;
  3811.       end else
  3812.       if not (foOverlapping in Options) and not (foNonMatch in Options) then
  3813.         Result := LastPos + FindLen else
  3814.         Result := LastPos + 1;
  3815.   End;
  3816.  
  3817.  
  3818.  
  3819. {                                                                              }
  3820. { FindFirst/FindNext                                                           }
  3821. {                                                                              }
  3822. Function FindFirstPos (var Iterator : TFindStringIterator; const Find, S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Integer;
  3823. var L : Integer;
  3824.   Begin
  3825.     With Iterator.Iter do
  3826.       begin
  3827.         FS := S;
  3828.         FOptions := Options;
  3829.         FMaxCount := MaxCount;
  3830.         if (MaxCount = 0) or not TranslateStartStop (S, Start, Stop, L, FStartIndex, FStopIndex) then
  3831.           begin
  3832.             Index := 0;
  3833.             Count := 0;
  3834.             Result := 0;
  3835.             exit;
  3836.           end;
  3837.  
  3838.         Result := Pos (Find, S, Options, Start, Stop);
  3839.         Index := Result;
  3840.         if Result > 0 then
  3841.           begin
  3842.             Iterator.FFind := Find;
  3843.             Count := 1;
  3844.           end else
  3845.           Count := 0;
  3846.       end;
  3847.   End;
  3848.  
  3849. Function FindNextPos (var Iterator : TFindStringIterator) : Integer;
  3850.   Begin
  3851.     With Iterator do
  3852.       With Iter do
  3853.         if Count = FMaxCount then
  3854.           Result := 0 else
  3855.           begin
  3856.             if foReverse in FOptions then
  3857.               begin
  3858.                 if not (foOverlapping in FOptions) and not (foNonMatch in FOptions) then
  3859.                   Dec (Index, Length (FFind)) else
  3860.                   Dec (Index);
  3861.                 Index := Pos (FFind, FS, FOptions, FStartIndex, Index);
  3862.               end else
  3863.               begin
  3864.                 if not (foOverlapping in FOptions) and not (foNonMatch in FOptions) then
  3865.                   Inc (Index, Length (FFind)) else
  3866.                   Inc (Index);
  3867.                 Index := Pos (FFind, FS, FOptions, Index, FStopIndex);
  3868.               end;
  3869.             Result := Index;
  3870.             if Result > 0 then
  3871.               Inc (Count);
  3872.           end;
  3873.   End;
  3874.  
  3875. Function FindFirstPosSeq (var Iterator : TFindCharSetArrayIterator; const Find : Array of CharSet; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Integer;
  3876. var L : Integer;
  3877.   Begin
  3878.     With Iterator.Iter do
  3879.       begin
  3880.         FS := S;
  3881.         FOptions := Options;
  3882.         FMaxCount := MaxCount;
  3883.         if (MaxCount = 0) or not TranslateStartStop (S, Start, Stop, L, FStartIndex, FStopIndex) then
  3884.           begin
  3885.             Index := 0;
  3886.             Count := 0;
  3887.             Result := 0;
  3888.             exit;
  3889.           end;
  3890.  
  3891.         Result := PosSeq (Find, S, Options, Start, Stop);
  3892.         Index := Result;
  3893.         if Result > 0 then
  3894.           begin
  3895.             Iterator.FFind := AsCharSetArray (Find);
  3896.             Count := 1;
  3897.           end else
  3898.           Count := 0;
  3899.       end;
  3900.   End;
  3901.  
  3902. Function FindNextPosSeq (var Iterator : TFindCharSetArrayIterator) : Integer;
  3903.   Begin
  3904.     With Iterator do
  3905.       With Iter do
  3906.         if Count = FMaxCount then
  3907.           Result := 0 else
  3908.           begin
  3909.             if foReverse in FOptions then
  3910.               begin
  3911.                 if not (foOverlapping in FOptions) and not (foNonMatch in FOptions) then
  3912.                   Dec (Index, Length (FFind)) else
  3913.                   Dec (Index);
  3914.                 Index := PosSeq (FFind, FS, FOptions, FStartIndex, Index);
  3915.               end else
  3916.               begin
  3917.                 if not (foOverlapping in FOptions) and not (foNonMatch in FOptions) then
  3918.                   Inc (Index, Length (FFind)) else
  3919.                   Inc (Index);
  3920.                 Index := PosSeq (FFind, FS, FOptions, Index, FStopIndex);
  3921.               end;
  3922.             Result := Index;
  3923.             if Result > 0 then
  3924.               Inc (Count);
  3925.           end;
  3926.   End;
  3927.  
  3928. Function FindFirstPos (var Iterator : TFindCharIterator; const Find : Char; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Integer;
  3929. var L : Integer;
  3930.   Begin
  3931.     With Iterator.Iter do
  3932.       begin
  3933.         FS := S;
  3934.         FOptions := Options;
  3935.         FMaxCount := MaxCount;
  3936.         if (MaxCount = 0) or not TranslateStartStop (S, Start, Stop, L, FStartIndex, FStopIndex) then
  3937.           begin
  3938.             Index := 0;
  3939.             Count := 0;
  3940.             Result := 0;
  3941.             exit;
  3942.           end;
  3943.  
  3944.         Result := Pos (Find, S, Options, Start, Stop);
  3945.         Index := Result;
  3946.         if Result > 0 then
  3947.           begin
  3948.             Iterator.FFind := Find;
  3949.             Count := 1;
  3950.           end else
  3951.           Count := 0;
  3952.       end;
  3953.   End;
  3954.  
  3955. Function FindNextPos (var Iterator : TFindCharIterator) : Integer;
  3956.   Begin
  3957.     With Iterator do
  3958.       With Iter do
  3959.         if Count = FMaxCount then
  3960.           Result := 0 else
  3961.           begin
  3962.             if foReverse in FOptions then
  3963.               begin
  3964.                 Dec (Index);
  3965.                 Index := Pos (FFind, FS, FOptions, FStartIndex, Index);
  3966.               end else
  3967.               begin
  3968.                 Inc (Index);
  3969.                 Index := Pos (FFind, FS, FOptions, Index, FStopIndex);
  3970.               end;
  3971.             Result := Index;
  3972.             if Result > 0 then
  3973.               Inc (Count);
  3974.           end;
  3975.   End;
  3976.  
  3977. Function FindFirstPos (var Iterator : TFindCharSetIterator; const Find : CharSet; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Integer;
  3978. var L : Integer;
  3979.   Begin
  3980.     With Iterator.Iter do
  3981.       begin
  3982.         FS := S;
  3983.         FOptions := Options;
  3984.         FMaxCount := MaxCount;
  3985.         if (MaxCount = 0) or not TranslateStartStop (S, Start, Stop, L, FStartIndex, FStopIndex) then
  3986.           begin
  3987.             Index := 0;
  3988.             Count := 0;
  3989.             Result := 0;
  3990.             exit;
  3991.           end;
  3992.  
  3993.         Result := Pos (Find, S, Options, Start, Stop);
  3994.         Index := Result;
  3995.         if Result > 0 then
  3996.           begin
  3997.             Iterator.FFind := Find;
  3998.             Count := 1;
  3999.           end else
  4000.           Count := 0;
  4001.       end;
  4002.   End;
  4003.  
  4004. Function FindNextPos (var Iterator : TFindCharSetIterator) : Integer;
  4005.   Begin
  4006.     With Iterator do
  4007.       With Iter do
  4008.         if Count = FMaxCount then
  4009.           Result := 0 else
  4010.           begin
  4011.             if foReverse in FOptions then
  4012.               begin
  4013.                 Dec (Index);
  4014.                 Index := Pos (FFind, FS, FOptions, FStartIndex, Index);
  4015.               end else
  4016.               begin
  4017.                 Inc (Index);
  4018.                 Index := Pos (FFind, FS, FOptions, Index, FStopIndex);
  4019.               end;
  4020.             Result := Index;
  4021.             if Result > 0 then
  4022.               Inc (Count);
  4023.           end;
  4024.   End;
  4025.  
  4026. Function FindFirstPos (var Iterator : TFindItemIterator; const Find : Array of String; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Integer;
  4027. var L : Integer;
  4028.   Begin
  4029.     With Iterator.Iter do
  4030.       begin
  4031.         FS := S;
  4032.         FOptions := Options;
  4033.         FMaxCount := MaxCount;
  4034.         if (MaxCount = 0) or not TranslateStartStop (S, Start, Stop, L, FStartIndex, FStopIndex) then
  4035.           begin
  4036.             Index := 0;
  4037.             Count := 0;
  4038.             Result := 0;
  4039.             exit;
  4040.           end;
  4041.  
  4042.         Result := PosStrings (Find, S, Iterator.ItemIndex, Options, Start, Stop);
  4043.         Index := Result;
  4044.         if Result > 0 then
  4045.           Count := 1 else
  4046.           Count := 0;
  4047.       end;
  4048.   End;
  4049.  
  4050. Function FindNextPos (var Iterator : TFindItemIterator; const Find : Array of String) : Integer;
  4051.   Begin
  4052.     With Iterator do
  4053.       With Iter do
  4054.         if Count = FMaxCount then
  4055.           Result := 0 else
  4056.           begin
  4057.             if foReverse in FOptions then
  4058.               begin
  4059.                 if not (foOverlapping in FOptions) and not (foNonMatch in FOptions) then
  4060.                   Dec (Index, Length (Find [ItemIndex])) else
  4061.                   Dec (Index);
  4062.                 Index := PosStrings (Find, FS, ItemIndex, FOptions, FStartIndex, Index);
  4063.               end else
  4064.               begin
  4065.                 if not (foOverlapping in FOptions) and not (foNonMatch in FOptions) then
  4066.                   Inc (Index, Length (Find [ItemIndex])) else
  4067.                   Inc (Index);
  4068.                 Index := PosStrings (Find, FS, ItemIndex, FOptions, Index, FStopIndex);
  4069.               end;
  4070.             Result := Index;
  4071.             if Result > 0 then
  4072.               Inc (Count);
  4073.           end;
  4074.   End;
  4075.  
  4076. Function FindFirstPos (var Iterator : TFindItemIterator; const Find : Array of Char; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Integer;
  4077. var L : Integer;
  4078.   Begin
  4079.     With Iterator.Iter do
  4080.       begin
  4081.         FS := S;
  4082.         FOptions := Options;
  4083.         FMaxCount := MaxCount;
  4084.         if (MaxCount = 0) or not TranslateStartStop (S, Start, Stop, L, FStartIndex, FStopIndex) then
  4085.           begin
  4086.             Index := 0;
  4087.             Count := 0;
  4088.             Result := 0;
  4089.             exit;
  4090.           end;
  4091.  
  4092.         Result := PosChars (Find, S, Iterator.ItemIndex, Options, Start, Stop);
  4093.         Index := Result;
  4094.         if Result > 0 then
  4095.           Count := 1 else
  4096.           Count := 0;
  4097.       end;
  4098.   End;
  4099.  
  4100. Function FindNextPos (var Iterator : TFindItemIterator; const Find : Array of Char) : Integer;
  4101.   Begin
  4102.     With Iterator do
  4103.       With Iter do
  4104.         if Count = FMaxCount then
  4105.           Result := 0 else
  4106.           begin
  4107.             if foReverse in FOptions then
  4108.               begin
  4109.                 if not (foOverlapping in FOptions) and not (foNonMatch in FOptions) then
  4110.                   Dec (Index, Length (Find [ItemIndex])) else
  4111.                   Dec (Index);
  4112.                 Index := PosChars (Find, FS, ItemIndex, FOptions, FStartIndex, Index);
  4113.               end else
  4114.               begin
  4115.                 if not (foOverlapping in FOptions) and not (foNonMatch in FOptions) then
  4116.                   Inc (Index, Length (Find [ItemIndex])) else
  4117.                   Inc (Index);
  4118.                 Index := PosChars (Find, FS, ItemIndex, FOptions, Index, FStopIndex);
  4119.               end;
  4120.             Result := Index;
  4121.             if Result > 0 then
  4122.               Inc (Count);
  4123.           end;
  4124.   End;
  4125.  
  4126.  
  4127.  
  4128. {                                                                              }
  4129. { FindFirstUnmatchedRange/FindNextUnmatchedRange                               }
  4130. {                                                                              }
  4131. Procedure CalcUnmatchedRange (const Iterator : TFindIterator; const FindLen : Integer; const Index, LastIndex : Integer; var StartIndex, StopIndex : Integer);
  4132.   Begin
  4133.     if Index > 0 then
  4134.       if foReverse in Iterator.FOptions then
  4135.         begin
  4136.           StartIndex := Index + FindLen;
  4137.           StopIndex := LastIndex - 1;
  4138.         end else
  4139.         begin
  4140.           StartIndex := LastIndex + FindLen;
  4141.           StopIndex := Index - 1;
  4142.         end
  4143.     else
  4144.       if foReverse in Iterator.FOptions then
  4145.         begin
  4146.           StartIndex := Iterator.FStartIndex;
  4147.           StopIndex := LastIndex - 1;
  4148.         end else
  4149.         begin
  4150.           StartIndex := LastIndex + FindLen;
  4151.           StopIndex := Iterator.FStopIndex;
  4152.         end;
  4153.   End;
  4154.  
  4155. Function FindFirstUnmatchedRange (var Iterator : TFindStringIterator; var StartIndex, StopIndex : Integer; const Find, S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Boolean;
  4156. var Index, L : Integer;
  4157.   Begin
  4158.     if (Find = '') or not TranslateStartStop (S, Start, Stop, L, StartIndex, StopIndex) then
  4159.       begin
  4160.         Result := False;
  4161.         exit;
  4162.       end;
  4163.     Index := FindFirstPos (Iterator, Find, S, Options, Start, Stop, MaxCount);
  4164.     Result := True;
  4165.     if Index > 0 then
  4166.       if foReverse in Options then
  4167.         StartIndex := Index + Length (Find) else
  4168.         StopIndex := Index - 1;
  4169.   End;
  4170.  
  4171. Function FindNextUnmatchedRange (var Iterator : TFindStringIterator; var StartIndex, StopIndex : Integer) : Boolean;
  4172.   Begin
  4173.     With Iterator.Iter do
  4174.       begin
  4175.         Result := not ((Count = 0) or (Index = 0));
  4176.         if Result then
  4177.           CalcUnmatchedRange (Iterator.Iter, Length (Iterator.FFind), FindNextPos (Iterator), Index, StartIndex, StopIndex);
  4178.       end;
  4179.   End;
  4180.  
  4181. Function FindFirstUnmatchedRangeSeq (var Iterator : TFindCharSetArrayIterator; var StartIndex, StopIndex : Integer; const Find : Array of CharSet; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Boolean;
  4182. var Index, L : Integer;
  4183.   Begin
  4184.     if (Length (Find) = 0) or not TranslateStartStop (S, Start, Stop, L, StartIndex, StopIndex) then
  4185.       begin
  4186.         Result := False;
  4187.         exit;
  4188.       end;
  4189.     Index := FindFirstPosSeq (Iterator, Find, S, Options, Start, Stop, MaxCount);
  4190.     Result := True;
  4191.     if Index > 0 then
  4192.       if foReverse in Options then
  4193.         StartIndex := Index + Length (Find) else
  4194.         StopIndex := Index - 1;
  4195.   End;
  4196.  
  4197. Function FindNextUnmatchedRangeSeq (var Iterator : TFindCharSetArrayIterator; var StartIndex, StopIndex : Integer) : Boolean;
  4198.   Begin
  4199.     With Iterator.Iter do
  4200.       begin
  4201.         Result := not ((Count = 0) or (Index = 0));
  4202.         if Result then
  4203.           CalcUnmatchedRange (Iterator.Iter, Length (Iterator.FFind), FindNextPosSeq (Iterator), Index, StartIndex, StopIndex);
  4204.       end;
  4205.   End;
  4206.  
  4207. Function FindFirstUnmatchedRange (var Iterator : TFindCharIterator; var StartIndex, StopIndex : Integer; const Find : Char; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Boolean;
  4208. var Index, L : Integer;
  4209.   Begin
  4210.     if not TranslateStartStop (S, Start, Stop, L, StartIndex, StopIndex) then
  4211.       begin
  4212.         Result := False;
  4213.         exit;
  4214.       end;
  4215.     Index := FindFirstPos (Iterator, Find, S, Options, Start, Stop, MaxCount);
  4216.     Result := True;
  4217.     if Index > 0 then
  4218.       if foReverse in Options then
  4219.         StartIndex := Index + 1 else
  4220.         StopIndex := Index - 1;
  4221.   End;
  4222.  
  4223. Function FindNextUnmatchedRange (var Iterator : TFindCharIterator; var StartIndex, StopIndex : Integer) : Boolean;
  4224.   Begin
  4225.     With Iterator.Iter do
  4226.       begin
  4227.         Result := not ((Count = 0) or (Index = 0));
  4228.         if Result then
  4229.           CalcUnmatchedRange (Iterator.Iter, Length (Iterator.FFind), FindNextPos (Iterator), Index, StartIndex, StopIndex);
  4230.       end;
  4231.   End;
  4232.  
  4233. Function FindFirstUnmatchedRange (var Iterator : TFindCharSetIterator; var StartIndex, StopIndex : Integer; const Find : CharSet; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Boolean;
  4234. var Index, L : Integer;
  4235.   Begin
  4236.     if not TranslateStartStop (S, Start, Stop, L, StartIndex, StopIndex) then
  4237.       begin
  4238.         Result := False;
  4239.         exit;
  4240.       end;
  4241.     Index := FindFirstPos (Iterator, Find, S, Options, Start, Stop, MaxCount);
  4242.     Result := True;
  4243.     if Index > 0 then
  4244.       if foReverse in Options then
  4245.         StartIndex := Index + 1 else
  4246.         StopIndex := Index - 1;
  4247.   End;
  4248.  
  4249. Function FindNextUnmatchedRange (var Iterator : TFindCharSetIterator; var StartIndex, StopIndex : Integer) : Boolean;
  4250.   Begin
  4251.     With Iterator.Iter do
  4252.       begin
  4253.         Result := not ((Count = 0) or (Index = 0));
  4254.         if Result then
  4255.           CalcUnmatchedRange (Iterator.Iter, 1, FindNextPos (Iterator), Index, StartIndex, StopIndex);
  4256.       end;
  4257.   End;
  4258.  
  4259. Function FindFirstUnmatchedRange (var Iterator : TFindItemIterator; var StartIndex, StopIndex : Integer; const Find : Array of String; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Boolean;
  4260. var Index, L : Integer;
  4261.   Begin
  4262.     if (High (Find) < 0) or not TranslateStartStop (S, Start, Stop, L, StartIndex, StopIndex) then
  4263.       begin
  4264.         Result := False;
  4265.         exit;
  4266.       end;
  4267.     Index := FindFirstPos (Iterator, Find, S, Options, Start, Stop, MaxCount);
  4268.     Result := True;
  4269.     if Index > 0 then
  4270.       if foReverse in Options then
  4271.         StartIndex := Index + Length (Find [Iterator.ItemIndex]) else
  4272.         StopIndex := Index - 1;
  4273.   End;
  4274.  
  4275. Function FindNextUnmatchedRange (var Iterator : TFindItemIterator; var StartIndex, StopIndex : Integer; const Find : Array of String) : Boolean;
  4276.   Begin
  4277.     With Iterator.Iter do
  4278.       begin
  4279.         Result := not ((Count = 0) or (Index = 0));
  4280.         if Result then
  4281.           CalcUnmatchedRange (Iterator.Iter, Length (Find [Iterator.ItemIndex]), FindNextPos (Iterator, Find), Index, StartIndex, StopIndex);
  4282.       end;
  4283.   End;
  4284.  
  4285.  
  4286.  
  4287. {                                                                              }
  4288. { Match Visitor functions                                                      }
  4289. {   Handles iterating through matches.                                         }
  4290. {                                                                              }
  4291. Function IterateMatches (const VisitProcedure : TMatchVisitProcedure; const Data : Pointer; const Find, S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Integer; overload;
  4292. var Index : Integer;
  4293.     Continue : Boolean;
  4294.     Iterator : TFindStringIterator;
  4295.   Begin
  4296.     Continue := True;
  4297.     Index := FindFirstPos (Iterator, Find, S, Options, Start, Stop, MaxCount);
  4298.     While Index > 0 do
  4299.       begin
  4300.         if Assigned (VisitProcedure) then
  4301.           begin
  4302.             With Iterator.Iter do
  4303.               VisitProcedure (Count, Index, Data, Continue, Iterator.Iter);
  4304.             if not Continue then
  4305.               break;
  4306.           end;
  4307.         Index := FindNextPos (Iterator);
  4308.       end;
  4309.     Result := Iterator.Iter.Count;
  4310.   End;
  4311.  
  4312. Function IterateMatchesSeq (const VisitProcedure : TMatchVisitProcedure; const Data : Pointer; const Find : Array of CharSet; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Integer; overload;
  4313. var Index : Integer;
  4314.     Continue : Boolean;
  4315.     Iterator : TFindCharSetArrayIterator;
  4316.   Begin
  4317.     Continue := True;
  4318.     Index := FindFirstPosSeq (Iterator, Find, S, Options, Start, Stop, MaxCount);
  4319.     While Index > 0 do
  4320.       begin
  4321.         if Assigned (VisitProcedure) then
  4322.           begin
  4323.             With Iterator.Iter do
  4324.               VisitProcedure (Count, Index, Data, Continue, Iterator.Iter);
  4325.             if not Continue then
  4326.               break;
  4327.           end;
  4328.         Index := FindNextPosSeq (Iterator);
  4329.       end;
  4330.     Result := Iterator.Iter.Count;
  4331.   End;
  4332.  
  4333. Function IterateMatches (const VisitProcedure : TMatchVisitProcedure; const Data : Pointer; const Find : Char; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Integer; overload;
  4334. var Index : Integer;
  4335.     Continue : Boolean;
  4336.     Iterator : TFindCharIterator;
  4337.   Begin
  4338.     Continue := True;
  4339.     Index := FindFirstPos (Iterator, Find, S, Options, Start, Stop, MaxCount);
  4340.     While Index > 0 do
  4341.       begin
  4342.         if Assigned (VisitProcedure) then
  4343.           begin
  4344.             With Iterator.Iter do
  4345.               VisitProcedure (Count, Index, Data, Continue, Iterator.Iter);
  4346.             if not Continue then
  4347.               break;
  4348.           end;
  4349.         Index := FindNextPos (Iterator);
  4350.       end;
  4351.     Result := Iterator.Iter.Count;
  4352.   End;
  4353.  
  4354. Function IterateMatches (const VisitProcedure : TMatchVisitProcedure; const Data : Pointer; const Find : CharSet; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Integer; overload;
  4355. var Index : Integer;
  4356.     Continue : Boolean;
  4357.     Iterator : TFindCharSetIterator;
  4358.   Begin
  4359.     Continue := True;
  4360.     Index := FindFirstPos (Iterator, Find, S, Options, Start, Stop, MaxCount);
  4361.     While Index > 0 do
  4362.       begin
  4363.         if Assigned (VisitProcedure) then
  4364.           begin
  4365.             With Iterator.Iter do
  4366.               VisitProcedure (Count, Index, Data, Continue, Iterator.Iter);
  4367.             if not Continue then
  4368.               break;
  4369.           end;
  4370.         Index := FindNextPos (Iterator);
  4371.       end;
  4372.     Result := Iterator.Iter.Count;
  4373.   End;
  4374.  
  4375. Function IterateMatches (const VisitProcedure : TMatchVisitProcedure; const Data : Pointer; const Find : Array of String; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Integer;
  4376. var Index : Integer;
  4377.     Continue : Boolean;
  4378.     Iterator : TFindItemIterator;
  4379.   Begin
  4380.     Continue := True;
  4381.     Index := FindFirstPos (Iterator, Find, S, Options, Start, Stop, MaxCount);
  4382.     While Index > 0 do
  4383.       begin
  4384.         if Assigned (VisitProcedure) then
  4385.           begin
  4386.             With Iterator.Iter do
  4387.               VisitProcedure (Count, Index, Data, Continue, Iterator.Iter);
  4388.             if not Continue then
  4389.               break;
  4390.           end;
  4391.         Index := FindNextPos (Iterator, Find);
  4392.       end;
  4393.     Result := Iterator.Iter.Count;
  4394.   End;
  4395.  
  4396.  
  4397.  
  4398. {                                                                              }
  4399. { Count                                                                        }
  4400. {                                                                              }
  4401. Function Count (const Find, S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Integer;
  4402.   Begin
  4403.     Result := IterateMatches (nil, nil, Find, S, Options, Start, Stop, MaxCount);
  4404.   End;
  4405.  
  4406. Function CountSeq (const Find : Array of CharSet; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Integer;
  4407.   Begin
  4408.     Result := IterateMatchesSeq (nil, nil, Find, S, Options, Start, Stop, MaxCount);
  4409.   End;
  4410.  
  4411. {$IFDEF WINTEL}
  4412. { Q_CharsCount by Andrew N. Driazgov (andrey@asp.tstu.ru)                      }
  4413. { Optimized version of the general Count (CharSet) case.                       }
  4414. Function Q_CharsCount (const S : String; const CharSet : CharSet) : Integer;
  4415.   Asm
  4416.         TEST    EAX, EAX
  4417.         JE      @@qt
  4418.         MOV     ECX, [EAX - 4]
  4419.         TEST    ECX, ECX
  4420.         JE      @@zq
  4421.         PUSH    EBX
  4422.         PUSH    ESI
  4423.         LEA     EBX, [EAX - 1]
  4424.         XOR     EAX, EAX
  4425. @@lp:   MOVZX   ESI, BYTE PTR [EBX + ECX]
  4426.         BT      [EDX], ESI
  4427.         JC      @@fn
  4428.         DEC     ECX
  4429.         JNE     @@lp
  4430.         POP     ESI
  4431.         POP     EBX
  4432.         RET
  4433. @@fn:   INC     EAX
  4434.         DEC     ECX
  4435.         JNE     @@lp
  4436.         POP     ESI
  4437.         POP     EBX
  4438.         RET
  4439. @@zq:   XOR     EAX, EAX
  4440. @@qt:
  4441.   End;
  4442. {$ENDIF}
  4443.  
  4444. Function Count (const Find : CharSet; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Integer;
  4445. var L : Integer;
  4446.   Begin
  4447.     {$IFDEF WINTEL}
  4448.     L := Length (S);
  4449.     if (Options = []) and ((Start = 1) or (Start = -L)) and ((Stop = -1) or (Stop = L)) and (MaxCount < 0) then // Optimization for the general case
  4450.       begin
  4451.         Result := Q_CharsCount (S, Find);
  4452.         exit;
  4453.       end;
  4454.     {$ENDIF}
  4455.     Result := IterateMatches (nil, nil, Find, S, Options, Start, Stop, MaxCount);
  4456.   End;
  4457.  
  4458. {$IFDEF WINTEL}
  4459. { Q_CharCount by Andrew N. Driazgov (andrey@asp.tstu.ru)                       }
  4460. { Optimized version of the general Count (Char) case.                          }
  4461. Function Q_CharCount (const S : String; const Ch : Char) : Integer;
  4462.   Asm
  4463.         TEST    EAX, EAX
  4464.         JE      @@qt
  4465.         MOV     ECX, [EAX - 4]
  4466.         TEST    ECX, ECX
  4467.         JE      @@zq
  4468.         PUSH    EBX
  4469.         LEA     EBX, [EAX - 1]
  4470.         XOR     EAX, EAX
  4471. @@lp:   CMP     DL, BYTE PTR [EBX + ECX]
  4472.         JE      @@fn
  4473.         DEC     ECX
  4474.         JNE     @@lp
  4475.         POP     EBX
  4476.         RET
  4477. @@fn:   INC     EAX
  4478.         DEC     ECX
  4479.         JNE     @@lp
  4480.         POP     EBX
  4481.         RET
  4482. @@zq:   XOR     EAX, EAX
  4483. @@qt:
  4484.   end;
  4485. {$ENDIF}
  4486.  
  4487. Function Count (const Find : Char; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Integer;
  4488. var L : Integer;
  4489.   Begin
  4490.     {$IFDEF WINTEL}
  4491.     L := Length (S);
  4492.     if (Options = []) and ((Start = 1) or (Start = -L)) and ((Stop = -1) or (Stop = L)) and (MaxCount < 0) then // Optimization for the general case
  4493.       begin
  4494.         Result := Q_CharCount (S, Find);
  4495.         exit;
  4496.       end;
  4497.     {$ENDIF}
  4498.     Result := IterateMatches (nil, nil, Find, S, Options, Start, Stop, MaxCount);
  4499.   End;
  4500.  
  4501. Function Count (const Find : Array of String; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : Integer;
  4502.   Begin
  4503.     Result := IterateMatches (nil, nil, Find, S, Options, Start, Stop, MaxCount);
  4504.   End;
  4505.  
  4506.  
  4507.  
  4508. {                                                                              }
  4509. { PosEx                                                                        }
  4510. {                                                                              }
  4511. Procedure PosExVisitProcedure (const Nr, Index : Integer; const Data : Pointer; var Continue : Boolean; const Iterator : TFindIterator);
  4512. var Result : ^Integer absolute Data;
  4513.   Begin
  4514.     Result^ := Index;
  4515.   End;
  4516.  
  4517. Function PosEx (const Find, S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const Count : Integer) : Integer;
  4518.   Begin
  4519.     if IterateMatches (PosExVisitProcedure, @Result, Find, S, Options, Start, Stop, Count) < Count then
  4520.       Result := 0;
  4521.   End;
  4522.  
  4523. Function PosEx (const Find : CharSet; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const Count : Integer) : Integer;
  4524.   Begin
  4525.     if IterateMatches (PosExVisitProcedure, @Result, Find, S, Options, Start, Stop, Count) < Count then
  4526.       Result := 0;
  4527.   End;
  4528.  
  4529. Function PosEx (const Find : Char; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const Count : Integer) : Integer;
  4530.   Begin
  4531.     if IterateMatches (PosExVisitProcedure, @Result, Find, S, Options, Start, Stop, Count) < Count then
  4532.       Result := 0;
  4533.   End;
  4534.  
  4535. Function PosExSeq (const Find : Array of CharSet; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const Count : Integer) : Integer;
  4536.   Begin
  4537.     if IterateMatchesSeq (PosExVisitProcedure, @Result, Find, S, Options, Start, Stop, Count) < Count then
  4538.       Result := 0;
  4539.   End;
  4540.  
  4541.  
  4542.  
  4543. {                                                                              }
  4544. { FindAll                                                                      }
  4545. {                                                                              }
  4546. Function DoFindAllSingleAllocation (var R : IntegerArray; const Count : Integer) : Boolean;
  4547.   Begin
  4548.     SetLength (R, Count);
  4549.     Result := Count > 0;
  4550.   End;
  4551.  
  4552. Function FindAll (const Find, S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer; const Algorithm : TFindAllAlgorithm) : IntegerArray;
  4553. var Iterator : TFindStringIterator;
  4554.   Begin
  4555.     if Algorithm = faSingleIteration then
  4556.       SetLength (Result, 0) else
  4557.       if not DoFindAllSingleAllocation (Result, Count (Find, S, Options, Start, Stop, MaxCount)) then
  4558.         exit;
  4559.     if FindFirstPos (Iterator, Find, S, Options, Start, Stop, MaxCount) > 0 then
  4560.       Repeat
  4561.         if Algorithm = faSingleAllocation then
  4562.           Result [Iterator.Iter.Count - 1] := Iterator.Iter.Index else
  4563.           Append (Result, Iterator.Iter.Index);
  4564.       Until FindNextPos (Iterator) = 0;
  4565.   End;
  4566.  
  4567. Function FindAll (const Find : Char; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer; const Algorithm : TFindAllAlgorithm) : IntegerArray;
  4568. var Iterator : TFindCharIterator;
  4569.   Begin
  4570.     if Algorithm = faSingleIteration then
  4571.       SetLength (Result, 0) else
  4572.       if not DoFindAllSingleAllocation (Result, Count (Find, S, Options, Start, Stop, MaxCount)) then
  4573.         exit;
  4574.     if FindFirstPos (Iterator, Find, S, Options, Start, Stop, MaxCount) > 0 then
  4575.       Repeat
  4576.         if Algorithm = faSingleAllocation then
  4577.           Result [Iterator.Iter.Count - 1] := Iterator.Iter.Index else
  4578.           Append (Result, Iterator.Iter.Index);
  4579.       Until FindNextPos (Iterator) = 0;
  4580.   End;
  4581.  
  4582. Function FindAll (const Find : CharSet; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer; const Algorithm : TFindAllAlgorithm) : IntegerArray;
  4583. var Iterator : TFindCharSetIterator;
  4584.   Begin
  4585.     if Algorithm = faSingleIteration then
  4586.       SetLength (Result, 0) else
  4587.       if not DoFindAllSingleAllocation (Result, Count (Find, S, Options, Start, Stop, MaxCount)) then
  4588.         exit;
  4589.     if FindFirstPos (Iterator, Find, S, Options, Start, Stop, MaxCount) > 0 then
  4590.       Repeat
  4591.         if Algorithm = faSingleAllocation then
  4592.           Result [Iterator.Iter.Count - 1] := Iterator.Iter.Index else
  4593.           Append (Result, Iterator.Iter.Index);
  4594.       Until FindNextPos (Iterator) = 0;
  4595.   End;
  4596.  
  4597. Function FindAllSeq (const Find : Array of CharSet; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer; const Algorithm : TFindAllAlgorithm) : IntegerArray;
  4598. var Iterator : TFindCharSetArrayIterator;
  4599.   Begin
  4600.     if Algorithm = faSingleIteration then
  4601.       SetLength (Result, 0) else
  4602.       if not DoFindAllSingleAllocation (Result, CountSeq (Find, S, Options, Start, Stop, MaxCount)) then
  4603.         exit;
  4604.     if FindFirstPosSeq (Iterator, Find, S, Options, Start, Stop, MaxCount) > 0 then
  4605.       Repeat
  4606.         if Algorithm = faSingleAllocation then
  4607.           Result [Iterator.Iter.Count - 1] := Iterator.Iter.Index else
  4608.           Append (Result, Iterator.Iter.Index);
  4609.       Until FindNextPosSeq (Iterator) = 0;
  4610.   End;
  4611.  
  4612.  
  4613.  
  4614. {                                                                              }
  4615. { Split                                                                        }
  4616. {                                                                              }
  4617. Function DoSplitSingleAllocation (var R : StringArray; const Count : Integer; const S : String) : Boolean;
  4618.   Begin
  4619.     SetLength (R, Count + 1);
  4620.     if Count = 0 then
  4621.       begin
  4622.         Result := False;
  4623.         R [0] := S;
  4624.       end else
  4625.       Result := True;
  4626.   End;
  4627.  
  4628. Function Split (const S, Delimiter : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer; const Algorithm : TSplitAlgorithm) : StringArray;
  4629. var StartIndex, StopIndex : Integer;
  4630.     Iterator : TFindStringIterator;
  4631.     T : String;
  4632.   Begin
  4633.     if Algorithm = saSingleIteration then
  4634.       SetLength (Result, 0) else
  4635.       if not DoSplitSingleAllocation (Result, Count (Delimiter, S, Options, Start, Stop, MaxCount), S) then
  4636.         exit;
  4637.  
  4638.     if FindFirstUnmatchedRange (Iterator, StartIndex, StopIndex, Delimiter, S, Options, Start, Stop, MaxCount) then
  4639.       Repeat
  4640.         T := CopyRange (S, StartIndex, StopIndex);
  4641.         if Algorithm = saSingleAllocation then
  4642.           Result [Iterator.Iter.Count - 1] := T else
  4643.           Append (Result, T);
  4644.       Until not FindNextUnmatchedRange (Iterator, StartIndex, StopIndex);
  4645.   End;
  4646.  
  4647. Function Split (const S : String; const Delimiter : Char; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer; const Algorithm : TSplitAlgorithm) : StringArray;
  4648. var StartIndex, StopIndex : Integer;
  4649.     Iterator : TFindCharIterator;
  4650.     T : String;
  4651.   Begin
  4652.     if Algorithm = saSingleIteration then
  4653.       SetLength (Result, 0) else
  4654.       if not DoSplitSingleAllocation (Result, Count (Delimiter, S, Options, Start, Stop, MaxCount), S) then
  4655.         exit;
  4656.  
  4657.     if FindFirstUnmatchedRange (Iterator, StartIndex, StopIndex, Delimiter, S, Options, Start, Stop, MaxCount) then
  4658.       Repeat
  4659.         T := CopyRange (S, StartIndex, StopIndex);
  4660.         if Algorithm = saSingleAllocation then
  4661.           Result [Iterator.Iter.Count - 1] := T else
  4662.           Append (Result, T);
  4663.       Until not FindNextUnmatchedRange (Iterator, StartIndex, StopIndex);
  4664.   End;
  4665.  
  4666. Function Split (const S : String; const Delimiter : CharSet; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer; const Algorithm : TSplitAlgorithm) : StringArray;
  4667. var StartIndex, StopIndex : Integer;
  4668.     Iterator : TFindCharSetIterator;
  4669.     T : String;
  4670.   Begin
  4671.     if Algorithm = saSingleIteration then
  4672.       SetLength (Result, 0) else
  4673.       if not DoSplitSingleAllocation (Result, Count (Delimiter, S, Options, StartIndex, StopIndex, MaxCount), S) then
  4674.         exit;
  4675.  
  4676.     if FindFirstUnmatchedRange (Iterator, StartIndex, StopIndex, Delimiter, S, Options, Start, Stop, MaxCount) then
  4677.       Repeat
  4678.         T := CopyRange (S, StartIndex, StopIndex);
  4679.         if Algorithm = saSingleAllocation then
  4680.           Result [Iterator.Iter.Count - 1] := T else
  4681.           Append (Result, T);
  4682.       Until not FindNextUnmatchedRange (Iterator, StartIndex, StopIndex);
  4683.   End;
  4684.  
  4685. Function Join (const S : Array of String; const Delimiter : String; const Start : Integer) : String;
  4686. var I, L, D, C : Integer;
  4687.     P : PChar;
  4688.     T : String;
  4689.   Begin
  4690.     L := Length (S);
  4691.     if L = 0 then
  4692.       begin
  4693.         Result := '';
  4694.         exit;
  4695.       end;
  4696.  
  4697.     D := Length (Delimiter);
  4698.     SetLength (Result, StringArrayLength (S) + (L - 1) * D);
  4699.     P := Pointer (Result);
  4700.     For I := Start to L - 1 do
  4701.       begin
  4702.         if (I > Start) and (D > 0) then
  4703.           begin
  4704.             MoveMem (Pointer (Delimiter)^, P^, D);
  4705.             Inc (P, D);
  4706.           end;
  4707.         T := S [I];
  4708.         C := Length (T);
  4709.         if C > 0 then
  4710.           begin
  4711.             MoveMem (Pointer (T)^, P^, C);
  4712.             Inc (P, C);
  4713.           end;
  4714.       end;
  4715.   End;
  4716.  
  4717. Procedure Split (const S : String; const Delimiter : String; var LeftSide, RightSide : String; const DelimiterOptional : Boolean; const SplitPosition : TSplitPosition; const Options : TFindOptions; const Start : Integer; const Stop : Integer);
  4718. var L, I, J, F, D : Integer;
  4719.   Begin
  4720.     if (Delimiter = '') or not TranslateStartStop (S, Start, Stop, L, I, J) then
  4721.       begin
  4722.         LeftSide := '';
  4723.         RightSide := '';
  4724.         exit;
  4725.       end;
  4726.  
  4727.     F := Pos (Delimiter, S, Options, I, J);
  4728.     if F = 0 then
  4729.       begin
  4730.         RightSide := '';
  4731.         if DelimiterOptional then
  4732.           LeftSide := S else
  4733.           LeftSide := '';
  4734.       end else
  4735.       begin
  4736.         D := Length (Delimiter);
  4737.         if SplitPosition = splitLeft then
  4738.           LeftSide := CopyRange (S, I, F + D - 1) else
  4739.           LeftSide := CopyRange (S, I, F - 1);
  4740.         if SplitPosition = splitRight then
  4741.           RightSide := CopyRange (S, F, MaxI (J, F + D - 1)) else
  4742.           RightSide := CopyRange (S, F + D, J);
  4743.       end;
  4744.   End;
  4745.  
  4746. Procedure Split (const S : String; const Delimiter : CharSet; var LeftSide, RightSide : String; const DelimiterOptional : Boolean; const SplitPosition : TSplitPosition; const Options : TFindOptions; const Start : Integer; const Stop : Integer);
  4747. var L, I, J, F : Integer;
  4748.   Begin
  4749.     if (Delimiter = []) or not TranslateStartStop (S, Start, Stop, L, I, J) then
  4750.       begin
  4751.         LeftSide := '';
  4752.         RightSide := '';
  4753.         exit;
  4754.       end;
  4755.  
  4756.     F := Pos (Delimiter, S, Options, I, J);
  4757.     if F = 0 then
  4758.       begin
  4759.         RightSide := '';
  4760.         if DelimiterOptional then
  4761.           LeftSide := S else
  4762.           LeftSide := '';
  4763.       end else
  4764.       begin
  4765.         if SplitPosition = splitLeft then
  4766.           LeftSide := CopyRange (S, I, F) else
  4767.           LeftSide := CopyRange (S, I, F - 1);
  4768.         if SplitPosition = splitRight then
  4769.           RightSide := CopyRange (S, F, MaxI (J, F)) else
  4770.           RightSide := CopyRange (S, F + 1, J);
  4771.       end;
  4772.   End;
  4773.  
  4774. Function ExtractWords (const S : String; const WordChars : CharSet) : StringArray;
  4775. var P, Q : PChar;
  4776.     L, M : Integer;
  4777.     T    : String;
  4778.   Begin
  4779.     Result := nil;
  4780.  
  4781.     L := Length (S);
  4782.     P := Pointer (S);
  4783.     Q := P;
  4784.     M := 0;
  4785.     While L > 0 do
  4786.       if P^ in WordChars then
  4787.         begin
  4788.           Inc (P);
  4789.           Dec (L);
  4790.           Inc (M);
  4791.         end else
  4792.         begin
  4793.           if M > 0 then
  4794.             begin
  4795.               SetLength (T, M);
  4796.               MoveMem (Q^, Pointer (T)^, M);
  4797.               Append (Result, T);
  4798.             end;
  4799.           M := 0;
  4800.           Inc (P);
  4801.           Dec (L);
  4802.           Q := P;
  4803.         end;
  4804.     if M > 0 then
  4805.       begin
  4806.         SetLength (T, M);
  4807.         MoveMem (Q^, Pointer (T)^, M);
  4808.         Append (Result, T);
  4809.       end;
  4810.   End;
  4811.  
  4812.  
  4813.  
  4814. {                                                                              }
  4815. { Replace                                                                      }
  4816. {                                                                              }
  4817. type
  4818.   ReplaceCharVisitData = record
  4819.     Result  : String;
  4820.     Replace : Char;
  4821.   end;
  4822.  
  4823. Procedure ReplaceCharVisitProcedure (const Nr, Index : Integer; const Data : Pointer; var Continue : Boolean; const Iterator : TFindIterator);
  4824. var D : ^ReplaceCharVisitData absolute Data;
  4825.   Begin
  4826.     With D^ do
  4827.       Result [Index] := Replace;
  4828.   End;
  4829.  
  4830. Function Replace (const Find, Replace : Char; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : String;
  4831. var I, J : Integer;
  4832.     P, Q : PChar;
  4833.     C, D : Char;
  4834.     Data : ReplaceCharVisitData;
  4835.   Begin
  4836.     I := Length (S);
  4837.     if (Options = []) and ((Start = 1) or (Start = -I)) and ((Stop = -1) or (Stop = I)) and (MaxCount < 0) then // Optimization for the general case
  4838.       begin
  4839.         SetLength (Result, I);
  4840.         if I = 0 then
  4841.           exit;
  4842.         P := Pointer (Result);
  4843.         Q := Pointer (S);
  4844.         For J := 1 to I do
  4845.           begin
  4846.             C := Q^;
  4847.             if C = Find then
  4848.               D := Replace else
  4849.               D := C;
  4850.             P^ := D;
  4851.             Inc (P);
  4852.             Inc (Q);
  4853.           end;
  4854.         exit;
  4855.       end;
  4856.     Data.Result := S;
  4857.     Data.Replace := Replace;
  4858.     IterateMatches (ReplaceCharVisitProcedure, @Data, Find, S, Options, Start, Stop, MaxCount);
  4859.     Result := Data.Result;
  4860.   End;
  4861.  
  4862. Function Replace (const Find : CharSet; const Replace : Char; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : String;
  4863. var Data : ReplaceCharVisitData;
  4864.   Begin
  4865.     Data.Result := S;
  4866.     Data.Replace := Replace;
  4867.     IterateMatches (ReplaceCharVisitProcedure, @Data, Find, S, Options, Start, Stop, MaxCount);
  4868.     Result := Data.Result;
  4869.   End;
  4870.  
  4871. Function Replace (const Find : Char; const Replace, S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer; const Algorithm : TReplaceAlgorithm) : String;
  4872. var Iterator : TFindCharIterator;
  4873.     I, C, L, ResultIndex, StartIndex, StopIndex : Integer;
  4874.     Reverse : Boolean;
  4875.   Begin
  4876.     Reverse := foReverse in Options;
  4877.     if Algorithm = raSingleAllocation then
  4878.       begin
  4879.         TranslateStartStop(S, Start, Stop, L, StartIndex, StopIndex);
  4880.         C := Count (Find, S, Options, StartIndex, StopIndex, MaxCount);
  4881.         if C = 0 then
  4882.           begin
  4883.             Result := CopyRange (S, StartIndex, StopIndex);
  4884.             exit;
  4885.           end;
  4886.         I := Length (S) + C * (Length (Replace) - 1) - (L - (StopIndex - StartIndex + 1));
  4887.         SetLength (Result, I);
  4888.         if Result = '' then
  4889.           exit;
  4890.         ResultIndex := iif (Reverse, I, 1);
  4891.         if FindFirstUnmatchedRange (Iterator, StartIndex, StopIndex, Find, S, Options, Start, Stop, MaxCount) then
  4892.           Repeat
  4893.             Paste (S, Result, ResultIndex, Reverse, StartIndex, StopIndex);
  4894.             if Iterator.Iter.Index > 0 then
  4895.               Paste (Replace, Result, ResultIndex, Reverse);
  4896.           Until not FindNextUnmatchedRange (Iterator, StartIndex, StopIndex);
  4897.       end else
  4898.       begin
  4899.         Result := '';
  4900.         ResultIndex := 1;
  4901.         if FindFirstUnmatchedRange (Iterator, StartIndex, StopIndex, Find, S, Options, Start, Stop, MaxCount) then
  4902.           Repeat
  4903.             if Reverse then
  4904.               Result := iif (Iterator.Iter.Index > 0, Replace, '') + CopyRange (S, StartIndex, StopIndex) + Result else
  4905.               begin
  4906.                 I := MaxI (StopIndex - StartIndex + 1, 0) + iif (Iterator.Iter.Index > 0, Length (Replace), 0);
  4907.                 if I > 0 then
  4908.                   begin
  4909.                     SetLength (Result, Length (Result) + I);
  4910.                     Paste (S, Result, ResultIndex, False, StartIndex, StopIndex);
  4911.                     if Iterator.Iter.Index > 0 then
  4912.                       Paste (Replace, Result, ResultIndex);
  4913.                   end;
  4914.               end;
  4915.           Until not FindNextUnmatchedRange (Iterator, StartIndex, StopIndex);
  4916.       end;
  4917.   End;
  4918.  
  4919. Function Replace (const Find : CharSet; const Replace, S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer; const Algorithm : TReplaceAlgorithm) : String;
  4920. var Iterator : TFindCharSetIterator;
  4921.     I, C, L, ResultIndex, StartIndex, StopIndex : Integer;
  4922.     Reverse : Boolean;
  4923.   Begin
  4924.     Reverse := foReverse in Options;
  4925.     if Algorithm = raSingleAllocation then
  4926.       begin
  4927.         TranslateStartStop(S, Start, Stop, L, StartIndex, StopIndex);
  4928.         C := Count (Find, S, Options, StartIndex, StopIndex, MaxCount);
  4929.         if C = 0 then
  4930.           begin
  4931.             Result := CopyRange (S, StartIndex, StopIndex);
  4932.             exit;
  4933.           end;
  4934.         I := Length (S) + C * (Length (Replace) - 1) - (L - (StopIndex - StartIndex + 1));
  4935.         SetLength (Result, I);
  4936.         if Result = '' then
  4937.           exit;
  4938.         ResultIndex := iif (Reverse, I, 1);
  4939.         if FindFirstUnmatchedRange (Iterator, StartIndex, StopIndex, Find, S, Options, Start, Stop, MaxCount) then
  4940.           Repeat
  4941.             Paste (S, Result, ResultIndex, Reverse, StartIndex, StopIndex);
  4942.             if Iterator.Iter.Index > 0 then
  4943.               Paste (Replace, Result, ResultIndex, Reverse);
  4944.           Until not FindNextUnmatchedRange (Iterator, StartIndex, StopIndex);
  4945.       end else
  4946.       begin
  4947.         Result := '';
  4948.         ResultIndex := 1;
  4949.         if FindFirstUnmatchedRange (Iterator, StartIndex, StopIndex, Find, S, Options, Start, Stop, MaxCount) then
  4950.           Repeat
  4951.             if Reverse then
  4952.               Result := iif (Iterator.Iter.Index > 0, Replace, '') + CopyRange (S, StartIndex, StopIndex) + Result else
  4953.               begin
  4954.                 I := MaxI (StopIndex - StartIndex + 1, 0) + iif (Iterator.Iter.Index > 0, Length (Replace), 0);
  4955.                 if I > 0 then
  4956.                   begin
  4957.                     SetLength (Result, Length (Result) + I);
  4958.                     Paste (S, Result, ResultIndex, False, StartIndex, StopIndex);
  4959.                     if Iterator.Iter.Index > 0 then
  4960.                       Paste (Replace, Result, ResultIndex);
  4961.                   end;
  4962.               end;
  4963.           Until not FindNextUnmatchedRange (Iterator, StartIndex, StopIndex);
  4964.       end;
  4965.   End;
  4966.  
  4967. Function ReplaceSeq (const Find : Array of CharSet; const Replace, S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer; const Algorithm : TReplaceAlgorithm) : String;
  4968. var Iterator : TFindCharSetArrayIterator;
  4969.     I, C, L, ResultIndex, StartIndex, StopIndex : Integer;
  4970.     Reverse : Boolean;
  4971.   Begin
  4972.     Reverse := foReverse in Options;
  4973.     if Algorithm = raSingleAllocation then
  4974.       begin
  4975.         TranslateStartStop(S, Start, Stop, L, StartIndex, StopIndex);
  4976.         C := CountSeq (Find, S, Options, StartIndex, StopIndex, MaxCount);
  4977.         if C = 0 then
  4978.           begin
  4979.             Result := CopyRange (S, StartIndex, StopIndex);
  4980.             exit;
  4981.           end;
  4982.         I := Length (S) + C * (Length (Replace) - Length (Find)) - (L - (StopIndex - StartIndex + 1));
  4983.         SetLength (Result, I);
  4984.         if Result = '' then
  4985.           exit;
  4986.         ResultIndex := iif (Reverse, I, 1);
  4987.         if FindFirstUnmatchedRangeSeq (Iterator, StartIndex, StopIndex, Find, S, Options, Start, Stop, MaxCount) then
  4988.           Repeat
  4989.             Paste (S, Result, ResultIndex, Reverse, StartIndex, StopIndex);
  4990.             if Iterator.Iter.Index > 0 then
  4991.               Paste (Replace, Result, ResultIndex, Reverse);
  4992.           Until not FindNextUnmatchedRangeSeq (Iterator, StartIndex, StopIndex);
  4993.       end else
  4994.       begin
  4995.         Result := '';
  4996.         ResultIndex := 1;
  4997.         if FindFirstUnmatchedRangeSeq (Iterator, StartIndex, StopIndex, Find, S, Options, Start, Stop, MaxCount) then
  4998.           Repeat
  4999.             if Reverse then
  5000.               Result := iif (Iterator.Iter.Index > 0, Replace, '') + CopyRange (S, StartIndex, StopIndex) + Result else
  5001.               begin
  5002.                 I := MaxI (StopIndex - StartIndex + 1, 0) + iif (Iterator.Iter.Index > 0, Length (Replace), 0);
  5003.                 if I > 0 then
  5004.                   begin
  5005.                     SetLength (Result, Length (Result) + I);
  5006.                     Paste (S, Result, ResultIndex, False, StartIndex, StopIndex);
  5007.                     if Iterator.Iter.Index > 0 then
  5008.                       Paste (Replace, Result, ResultIndex);
  5009.                   end;
  5010.               end;
  5011.           Until not FindNextUnmatchedRangeSeq (Iterator, StartIndex, StopIndex);
  5012.       end;
  5013.   End;
  5014.  
  5015. {$IFDEF WINTEL}
  5016. { Quick version of the general replace case, adapted from a routine by         }
  5017. { Andrew N. Driazgov (andrey@asp.tstu.ru)                                      }
  5018. Function Q_ReplaceStr (const Find, Replace, S : String) : String;
  5019. var P, PS : PChar;
  5020.     L, L1, L2, Cnt : Integer;
  5021.     I, J, K, M : Integer;
  5022.   Begin
  5023.     L1 := Length (Find);
  5024.     Cnt := 0;
  5025.     I := Pos (Find, S);
  5026.     while I <> 0 do
  5027.       begin
  5028.         Inc (I, L1);
  5029.         asm
  5030.           PUSH    I
  5031.         end;
  5032.         Inc (Cnt);
  5033.         I := Pos (Find, S, [], I);
  5034.       end;
  5035.     if Cnt <> 0 then
  5036.       begin
  5037.         L := Length (S);
  5038.         L2 := Length (Replace);
  5039.         J := L + 1;
  5040.         Inc (L, (L2 - L1) * Cnt);
  5041.         if L <> 0 then
  5042.           begin
  5043.             SetString (Result, nil, L);
  5044.             P := Pointer (Result);
  5045.             Inc (P, L);
  5046.             PS := Pointer (LongWord (S) - 1);
  5047.             if L2 <= 32 then
  5048.               for I := 0 to Cnt - 1 do
  5049.                 begin
  5050.                   asm
  5051.                     POP     K
  5052.                   end;
  5053.                   M := J - K;
  5054.                   if M > 0 then
  5055.                   begin
  5056.                     Dec (P, M);
  5057.                     MoveMem (PS [K], P^, M);
  5058.                   end;
  5059.                   Dec (P, L2);
  5060.                   if L2 > 0 then
  5061.                     MoveMem (Pointer (Replace)^, P^, L2);
  5062.                   J := K - L1;
  5063.                 end
  5064.             else
  5065.               for I := 0 to Cnt-1 do
  5066.                 begin
  5067.                   asm
  5068.                     POP     K
  5069.                   end;
  5070.                   M := J - K;
  5071.                   if M > 0 then
  5072.                   begin
  5073.                     Dec (P, M);
  5074.                     MoveMem (PS [K], P^, M);
  5075.                   end;
  5076.                   Dec (P, L2);
  5077.                   if L2 > 0 then
  5078.                     MoveMem (Pointer (Replace)^, P^, L2);
  5079.                   J := K - L1;
  5080.                 end;
  5081.             Dec (J);
  5082.             if J > 0 then
  5083.               MoveMem (Pointer (S)^, Pointer (Result)^, J);
  5084.           end else
  5085.           Result := '';
  5086.       end else
  5087.       Result := S;
  5088.   End;
  5089. {$ENDIF}
  5090.  
  5091. Function Replace (const Find, Replace, S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer; const Algorithm : TReplaceAlgorithm) : String;
  5092. var Iterator : TFindStringIterator;
  5093.     I, C, L, ResultIndex, StartIndex, StopIndex : Integer;
  5094.     Reverse : Boolean;
  5095.   Begin
  5096.     {$IFDEF WINTEL}
  5097.     I := Length (S);
  5098.     if (Options = []) and ((Start = 1) or (Start = -I)) and ((Stop = -1) or (Stop = I)) and (MaxCount < 0) then // Optimization for the general case
  5099.       begin
  5100.         Result := Q_ReplaceStr (Find, Replace, S);
  5101.         exit;
  5102.       end;
  5103.     {$ENDIF}
  5104.     Reverse := foReverse in Options;
  5105.     if Algorithm = raSingleAllocation then
  5106.       begin
  5107.         TranslateStartStop(S, Start, Stop, L, StartIndex, StopIndex);
  5108.         C := Count (Find, S, Options, StartIndex, StopIndex, MaxCount);
  5109.         if C = 0 then
  5110.           begin
  5111.             Result := CopyRange(S, StartIndex, StopIndex);
  5112.             exit;
  5113.           end;
  5114.         I := Length (S) + C * (Length (Replace) - Length (Find)) - (L - (StopIndex - StartIndex + 1));
  5115.         SetLength (Result, I);
  5116.         if Result = '' then
  5117.           exit;
  5118.         ResultIndex := iif (Reverse, I, 1);
  5119.         if FindFirstUnmatchedRange (Iterator, StartIndex, StopIndex, Find, S, Options, Start, Stop, MaxCount) then
  5120.           Repeat
  5121.             Paste (S, Result, ResultIndex, Reverse, StartIndex, StopIndex);
  5122.             if Iterator.Iter.Index > 0 then
  5123.               Paste (Replace, Result, ResultIndex, Reverse);
  5124.           Until not FindNextUnmatchedRange (Iterator, StartIndex, StopIndex);
  5125.       end else
  5126.       begin
  5127.         Result := '';
  5128.         ResultIndex := 1;
  5129.         if FindFirstUnmatchedRange (Iterator, StartIndex, StopIndex, Find, S, Options, Start, Stop, MaxCount) then
  5130.           Repeat
  5131.             if Reverse then
  5132.               Result := iif (Iterator.Iter.Index > 0, Replace, '') + CopyRange (S, StartIndex, StopIndex) + Result else
  5133.               begin
  5134.                 I := MaxI (StopIndex - StartIndex + 1, 0) + iif (Iterator.Iter.Index > 0, Length (Replace), 0);
  5135.                 if I > 0 then
  5136.                   begin
  5137.                     SetLength (Result, Length (Result) + I);
  5138.                     Paste (S, Result, ResultIndex, False, StartIndex, StopIndex);
  5139.                     if Iterator.Iter.Index > 0 then
  5140.                       Paste (Replace, Result, ResultIndex);
  5141.                   end;
  5142.               end;
  5143.           Until not FindNextUnmatchedRange (Iterator, StartIndex, StopIndex);
  5144.       end;
  5145.   End;
  5146.  
  5147. Function ReplaceChars (const Find, Replace : Array of Char; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer; const MaxCount : Integer) : String;
  5148. var L, I, J, F, C : Integer;
  5149.     CaseSensitive : Boolean;
  5150.  
  5151.   Function CheckMatch (var Res : String) : Boolean;
  5152.   var G : Integer;
  5153.     Begin
  5154.       Result := True;
  5155.       G := MatchChars (S [F], Find, CaseSensitive);
  5156.       if G >= 0 then
  5157.         begin
  5158.           Res [F] := Replace [G];
  5159.           if MaxCount > 0 then
  5160.             begin
  5161.               Inc (C);
  5162.               if C = MaxCount then
  5163.                 Result := False;
  5164.             end;
  5165.         end;
  5166.     End;
  5167.  
  5168.   Begin
  5169.     Assert (High (Find) = High (Replace), 'Find and Replace arrays must be of equal length');
  5170.     Result := S;
  5171.     if (MaxCount = 0) or not TranslateStartStop (S, Start, Stop, L, I, J) then
  5172.       exit;
  5173.     CaseSensitive := not (foCaseInsensitive in Options);
  5174.     C := 0;
  5175.     if foReverse in Options then
  5176.       begin
  5177.         For F := J downto I do
  5178.           if not CheckMatch (Result) then
  5179.             exit;
  5180.       end else
  5181.       For F := I to J do
  5182.         if not CheckMatch (Result) then
  5183.           exit;
  5184.   End;
  5185.  
  5186. Function RemoveSeq (const C : Array of CharSet; const S : String) : String;
  5187.   Begin
  5188.     Result := ReplaceSeq (C, '', S);
  5189.   End;
  5190.  
  5191. Function RemoveDup (const C : Char; const S : String) : String;
  5192. var P, Q : PChar;
  5193.     D, E : Char;
  5194.     I, L, M : Integer;
  5195.   Begin
  5196.     L := Length (S);
  5197.     if L <= 1 then
  5198.       begin
  5199.         Result := S;
  5200.         exit;
  5201.       end;
  5202.     Result := S;
  5203.     SetLength (Result, L);
  5204.  
  5205.     P := Pointer (S);
  5206.     Q := Pointer (Result);
  5207.  
  5208.     D := P^;
  5209.     Q^ := D;
  5210.     Inc (P);
  5211.     Inc (Q);
  5212.  
  5213.     M := 1;
  5214.     For I := 2 to L do
  5215.       begin
  5216.         E := P^;
  5217.         if (D <> C) or (E <> C) then
  5218.           begin
  5219.             D := E;
  5220.             Q^ := E;
  5221.             Inc (M);
  5222.             Inc (Q);
  5223.           end;
  5224.         Inc (P);
  5225.       end;
  5226.     SetLength (Result, M);
  5227.   End;
  5228.  
  5229. {$IFDEF WINTEL}
  5230. { Q_DelChar by Andrew N. Driazgov (andrey@asp.tstu.ru)                         }
  5231. { Quick version of general RemoveAll (Char, S) case                            }
  5232. Function Q_DelChar (const S : String; Ch : Char) : String;
  5233.   Asm
  5234.         PUSH    ESI
  5235.         PUSH    EBX
  5236.         PUSH    EDI
  5237.         MOV     ESI, ECX
  5238.         TEST    EAX, EAX
  5239.         JE      @@qt
  5240.         MOV     ECX, [EAX - 4]
  5241.         TEST    ECX, ECX
  5242.         JE      @@qt
  5243.         MOV     EBX, EAX
  5244.         MOV     EDI, EDX
  5245.         XOR     EDX, EDX
  5246.         MOV     EAX, ESI
  5247.         CALL    System.@LStrFromPCharLen
  5248.         MOV     EDX,EDI
  5249.         MOV     ECX, [EBX-4]
  5250.         MOV     EDI, [ESI]
  5251. @@lp:   MOV     AL, BYTE PTR [EBX]
  5252.         CMP     DL, AL
  5253.         JE      @@nx
  5254.         MOV     BYTE PTR [EDI], AL
  5255.         INC     EDI
  5256. @@nx:   INC     EBX
  5257.         DEC     ECX
  5258.         JNE     @@lp
  5259.         MOV     EAX, [ESI]
  5260.         MOV     BYTE PTR [EDI],0
  5261.         SUB     EDI, EAX
  5262.         JE      @@qt
  5263.         MOV     [EAX-4], EDI
  5264.         POP     EDI
  5265.         POP     EBX
  5266.         POP     ESI
  5267.         RET
  5268. @@qt:   MOV     EAX,ESI
  5269.         CALL    System.@LStrClr
  5270.         POP     EDI
  5271.         POP     EBX
  5272.         POP     ESI
  5273.   End;
  5274. {$ENDIF}
  5275.  
  5276. Function RemoveAll (const Find : Char; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer;const Algorithm : TReplaceAlgorithm) : String;
  5277. var L : Integer;
  5278.   Begin
  5279.     {$IFDEF WINTEL}
  5280.     L := Length (S);
  5281.     if (Options = []) and ((Start = 1) or (Start = -L)) and ((Stop = -1) or (Stop = L)) then // Optimization for the general case
  5282.       begin
  5283.         Result := Q_DelChar (S, Find);
  5284.         exit;
  5285.       end;
  5286.     {$ENDIF}
  5287.     Result := Replace (Find, '', S, Options, Start, Stop, -1, Algorithm);
  5288.   End;
  5289.  
  5290. Function RemoveAll (const Find, S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer;const Algorithm : TReplaceAlgorithm) : String;
  5291.   Begin
  5292.     Result := Replace (Find, '', S, Options, Start, Stop, -1, Algorithm);
  5293.   End;
  5294.  
  5295. {$IFDEF WINTEL}
  5296. { Q_DelChars by Andrew N. Driazgov (andrey@asp.tstu.ru)                        }
  5297. { Quick version of general RemoveAll (CharSet, S) case                         }
  5298. Procedure Q_DelChars (var S : string; const CharsToRemove : CharSet);
  5299.   Asm
  5300.         PUSH    ESI
  5301.         PUSH    EDI
  5302.         MOV     ESI, EDX
  5303.         PUSH    EAX
  5304.         CALL    UniqueString
  5305.         TEST    EAX, EAX
  5306.         JE      @@qt
  5307.         MOV     ECX, [EAX - 4]
  5308.         MOV     EDI, EAX
  5309.         TEST    ECX, ECX
  5310.         JE      @@zq0
  5311. @@lp1:  MOVZX   EDX,BYTE PTR [EAX]
  5312.         BT      [ESI], EDX
  5313.         JC      @@rp
  5314. @@nx1:  MOV     BYTE PTR [EDI], DL
  5315.         INC     EAX
  5316.         INC     EDI
  5317.         DEC     ECX
  5318.         JNE     @@lp1
  5319. @@nx2:  POP     EAX
  5320.         MOV     ECX, [EAX]
  5321.         MOV     BYTE PTR [EDI], 0
  5322.         SUB     EDI, ECX
  5323.         JE      @@zq1
  5324.         MOV     [ECX - 4], EDI
  5325.         POP     EDI
  5326.         POP     ESI
  5327.         RET
  5328. @@qt:   POP     ECX
  5329.         POP     EDI
  5330.         POP     ESI
  5331.         RET
  5332. @@zq0:  POP     EAX
  5333. @@zq1:  CALL    System.@LStrClr
  5334.         POP     EDI
  5335.         POP     ESI
  5336.         RET
  5337. @@rp:   INC     EAX
  5338.         DEC     ECX
  5339.         JE      @@nx2
  5340. @@lp2:  MOVZX   EDX, BYTE PTR [EAX]
  5341.         BT      [ESI], EDX
  5342.         JNC     @@nx1
  5343.         INC     EAX
  5344.         DEC     ECX
  5345.         JNE     @@lp2
  5346.         JMP     @@nx2
  5347.   End;
  5348. {$ENDIF}
  5349.  
  5350. Function RemoveAll (const Find : CharSet; const S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer;const Algorithm : TReplaceAlgorithm) : String;
  5351. var L : Integer;
  5352.   Begin
  5353.     {$IFDEF WINTEL}
  5354.     L := Length (S);
  5355.     if (Options = []) and ((Start = 1) or (Start = -L)) and ((Stop = -1) or (Stop = L)) then // Optimization for the general case
  5356.       begin
  5357.         Result := S;
  5358.         Q_DelChars (Result, Find);
  5359.         exit;
  5360.       end;
  5361.     {$ENDIF}
  5362.     Result := Replace (Find, '', S, Options, Start, Stop, -1, Algorithm);
  5363.   End;
  5364.  
  5365. Function RemoveFirst (const Find, S : String; const Options : TFindOptions; const Start : Integer; const Stop : Integer;const Algorithm : TReplaceAlgorithm) : String;
  5366.   Begin
  5367.     Result := Replace (Find, '', S, Options, Start, Stop, 1, Algorithm);
  5368.   End;
  5369.  
  5370.  
  5371.  
  5372. {                                                                              }
  5373. { Reverse                                                                      }
  5374. {                                                                              }
  5375. Function Reversed (const S : String) : String;
  5376. var I, L : Integer;
  5377.     P, Q : PChar;
  5378.   Begin
  5379.     L := Length (S);
  5380.     if L = 0 then
  5381.       begin
  5382.         Result := '';
  5383.         exit;
  5384.       end;
  5385.     if L = 1 then
  5386.       begin
  5387.         Result := S;
  5388.         exit;
  5389.       end;
  5390.     SetLength (Result, L);
  5391.     P := Pointer (S);
  5392.     Q := Pointer (Result);
  5393.     Inc (Q, L - 1);
  5394.     For I := 1 to L do
  5395.       begin
  5396.         Q^ := P^;
  5397.         Dec (Q);
  5398.         Inc (P);
  5399.       end;
  5400.   End;
  5401.  
  5402.  
  5403.  
  5404. {                                                                              }
  5405. { Delimiter-based Copy                                                         }
  5406. {                                                                              }
  5407. Function CopyLeft (const S, Delimiter : String; const DelimiterOptions : TDelimiterOptions; const FindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const Count : Integer) : String;
  5408. var StartIndex, StopIndex, I : Integer;
  5409.   Begin
  5410.     if not TranslateStartStop (S, Start, Stop, I, StartIndex, StopIndex) then
  5411.       Result := '' else
  5412.       begin
  5413.         I := PosEx (Delimiter, S, FindOptions, StartIndex, StopIndex, Count);
  5414.         if I > 0 then
  5415.           if doIncludeDelimiter in DelimiterOptions then
  5416.             Result := CopyRange (S, StartIndex, I + Length (Delimiter) - 1) else
  5417.             Result := CopyRange (S, StartIndex, I - 1)
  5418.         else
  5419.           if (Count <= 0) or (doOptional in DelimiterOptions) then
  5420.             Result := CopyRange (S, StartIndex, StopIndex) else
  5421.             Result := '';
  5422.       end;
  5423.   End;
  5424.  
  5425. Function CopyLeft (const S : String; const Delimiter : CharSet; const DelimiterOptions : TDelimiterOptions; const FindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const Count : Integer) : String;
  5426. var StartIndex, StopIndex, I : Integer;
  5427.   Begin
  5428.     if not TranslateStartStop (S, Start, Stop, I, StartIndex, StopIndex) then
  5429.       Result := '' else
  5430.       begin
  5431.         I := PosEx (Delimiter, S, FindOptions, StartIndex, StopIndex, Count);
  5432.         if I > 0 then
  5433.           if doIncludeDelimiter in DelimiterOptions then
  5434.             Result := CopyRange (S, StartIndex, I) else
  5435.             Result := CopyRange (S, StartIndex, I - 1)
  5436.         else
  5437.           if (Count <= 0) or (doOptional in DelimiterOptions) then
  5438.             Result := CopyRange (S, StartIndex, StopIndex) else
  5439.             Result := '';
  5440.       end;
  5441.   End;
  5442.  
  5443. Function CopyRight (const S, Delimiter : String; const DelimiterOptions : TDelimiterOptions; const FindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const Count : Integer) : String;
  5444. var StartIndex, StopIndex, I : Integer;
  5445.   Begin
  5446.     if not TranslateStartStop (S, Start, Stop, I, StartIndex, StopIndex) then
  5447.       Result := '' else
  5448.       begin
  5449.         I := PosEx (Delimiter, S, FindOptions, StartIndex, StopIndex, Count);
  5450.         if I > 0 then
  5451.           if doIncludeDelimiter in DelimiterOptions then
  5452.             Result := CopyRange (S, I, StopIndex) else
  5453.             Result := CopyRange (S, I + Length (Delimiter), StopIndex)
  5454.         else
  5455.           if (Count <= 0) or (doOptional in DelimiterOptions) then
  5456.             Result := CopyRange (S, StartIndex, StopIndex) else
  5457.             Result := '';
  5458.       end;
  5459.   End;
  5460.  
  5461. Function CopyRight (const S : String; const Delimiter : CharSet; const DelimiterOptions : TDelimiterOptions; const FindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const Count : Integer) : String;
  5462. var StartIndex, StopIndex, I : Integer;
  5463.   Begin
  5464.     if not TranslateStartStop (S, Start, Stop, I, StartIndex, StopIndex) then
  5465.       Result := '' else
  5466.       begin
  5467.         I := PosEx (Delimiter, S, FindOptions, StartIndex, StopIndex, Count);
  5468.         if I > 0 then
  5469.           if doIncludeDelimiter in DelimiterOptions then
  5470.             Result := CopyRange (S, I, StopIndex) else
  5471.             Result := CopyRange (S, I + 1, StopIndex)
  5472.         else
  5473.           if (Count <= 0) or (doOptional in DelimiterOptions) then
  5474.             Result := CopyRange (S, StartIndex, StopIndex) else
  5475.             Result := '';
  5476.       end;
  5477.   End;
  5478.  
  5479. Function CopyRange (const S, LeftDelimiter, RightDelimiter : String; const LeftDelimiterOptions : TDelimiterOptions; const RightDelimiterOptions : TDelimiterOptions; const NotRange : Boolean; const LeftFindOptions : TFindOptions; const RightFindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const LeftCount : Integer; const RightCount : Integer) : String;
  5480. var StartIndex, StopIndex, I, J : Integer;
  5481.   Begin
  5482.     if not TranslateStartStop (S, Start, Stop, I, StartIndex, StopIndex) then
  5483.       Result := '' else
  5484.       begin
  5485.         I := PosEx (LeftDelimiter, S, LeftFindOptions, StartIndex, StopIndex, LeftCount);
  5486.         if I = 0 then
  5487.           if (LeftCount <= 0) or (doOptional in LeftDelimiterOptions) then
  5488.             I := StartIndex else
  5489.             begin
  5490.               Result := '';
  5491.               exit;
  5492.             end
  5493.         else
  5494.           if not (doIncludeDelimiter in LeftDelimiterOptions) then
  5495.             Inc (I, Length (LeftDelimiter));
  5496.  
  5497.         J := PosEx (RightDelimiter, S, RightFindOptions, I, StopIndex, RightCount);
  5498.         if J = 0 then
  5499.           if (RightCount <= 0) or (doOptional in RightDelimiterOptions) then
  5500.             J := StopIndex else
  5501.             begin
  5502.               Result := '';
  5503.               exit;
  5504.             end
  5505.         else
  5506.           if doIncludeDelimiter in RightDelimiterOptions then
  5507.             Inc (J, Length (RightDelimiter) - 1) else
  5508.             Dec (J);
  5509.  
  5510.         if NotRange then
  5511.           begin
  5512.             Result := CopyRange (S, StartIndex, StopIndex);
  5513.             Delete (Result, I - StartIndex + 1, J - I + 1);
  5514.           end else
  5515.           Result := CopyRange (S, I, J);
  5516.       end;
  5517.   End;
  5518.  
  5519. Function CopyRange (const S, LeftDelimiter : String; const RightDelimiter : CharSet; const LeftDelimiterOptions : TDelimiterOptions; const RightDelimiterOptions : TDelimiterOptions; const NotRange : Boolean; const LeftFindOptions : TFindOptions; const RightFindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const LeftCount : Integer; const RightCount : Integer) : String;
  5520. var StartIndex, StopIndex, I, J : Integer;
  5521.   Begin
  5522.     if not TranslateStartStop (S, Start, Stop, I, StartIndex, StopIndex) then
  5523.       Result := '' else
  5524.       begin
  5525.         I := PosEx (LeftDelimiter, S, LeftFindOptions, StartIndex, StopIndex, LeftCount);
  5526.         if I = 0 then
  5527.           if (LeftCount <= 0) or (doOptional in LeftDelimiterOptions) then
  5528.             I := StartIndex else
  5529.             begin
  5530.               Result := '';
  5531.               exit;
  5532.             end
  5533.         else
  5534.           if not (doIncludeDelimiter in LeftDelimiterOptions) then
  5535.             Inc (I, Length (LeftDelimiter));
  5536.  
  5537.         J := PosEx (RightDelimiter, S, RightFindOptions, I, StopIndex, RightCount);
  5538.         if J = 0 then
  5539.           if (RightCount <= 0) or (doOptional in RightDelimiterOptions) then
  5540.             J := StopIndex else
  5541.             begin
  5542.               Result := '';
  5543.               exit;
  5544.             end
  5545.         else
  5546.           if not (doIncludeDelimiter in RightDelimiterOptions) then
  5547.             Dec (J);
  5548.  
  5549.         if NotRange then
  5550.           begin                                              
  5551.             Result := CopyRange (S, StartIndex, StopIndex);
  5552.             Delete (Result, I - StartIndex + 1, J - I + 1);
  5553.           end else
  5554.           Result := CopyRange (S, I, J);
  5555.       end;
  5556.   End;
  5557.  
  5558. Function CopyRange (const S : String; const LeftDelimiter : CharSet; const RightDelimiter : String; const LeftDelimiterOptions : TDelimiterOptions; const RightDelimiterOptions : TDelimiterOptions; const NotRange : Boolean; const LeftFindOptions : TFindOptions; const RightFindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const LeftCount : Integer; const RightCount : Integer) : String;
  5559. var StartIndex, StopIndex, I, J : Integer;
  5560.   Begin
  5561.     if not TranslateStartStop (S, Start, Stop, I, StartIndex, StopIndex) then
  5562.       Result := '' else
  5563.       begin
  5564.         I := PosEx (LeftDelimiter, S, LeftFindOptions, StartIndex, StopIndex, LeftCount);
  5565.         if I = 0 then
  5566.           if (LeftCount <= 0) or (doOptional in LeftDelimiterOptions) then
  5567.             I := StartIndex else
  5568.             begin
  5569.               Result := '';
  5570.               exit;
  5571.             end
  5572.         else
  5573.           if not (doIncludeDelimiter in LeftDelimiterOptions) then
  5574.             Inc (I);
  5575.  
  5576.         J := PosEx (RightDelimiter, S, RightFindOptions, I, StopIndex, RightCount);
  5577.         if J = 0 then
  5578.           if (RightCount <= 0) or (doOptional in RightDelimiterOptions) then
  5579.             J := StopIndex else
  5580.             begin
  5581.               Result := '';
  5582.               exit;
  5583.             end
  5584.         else
  5585.           if doIncludeDelimiter in RightDelimiterOptions then
  5586.             Inc (J, Length (RightDelimiter) - 1) else
  5587.             Dec (J);
  5588.  
  5589.         if NotRange then
  5590.           begin
  5591.             Result := CopyRange (S, StartIndex, StopIndex);
  5592.             Delete (Result, I - StartIndex + 1, J - I + 1);
  5593.           end else
  5594.           Result := CopyRange (S, I, J);
  5595.       end;
  5596.   End;
  5597.  
  5598. Function CopyRange (const S : String; const LeftDelimiter, RightDelimiter : CharSet; const LeftDelimiterOptions : TDelimiterOptions; const RightDelimiterOptions : TDelimiterOptions; const NotRange : Boolean; const LeftFindOptions : TFindOptions; const RightFindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const LeftCount : Integer; const RightCount : Integer) : String;
  5599. var StartIndex, StopIndex, I, J : Integer;
  5600.   Begin
  5601.     if not TranslateStartStop (S, Start, Stop, I, StartIndex, StopIndex) then
  5602.       Result := '' else
  5603.       begin
  5604.         I := PosEx (LeftDelimiter, S, LeftFindOptions, StartIndex, StopIndex, LeftCount);
  5605.         if I = 0 then
  5606.           if (LeftCount <= 0) or (doOptional in LeftDelimiterOptions) then
  5607.             I := StartIndex else
  5608.             begin
  5609.               Result := '';
  5610.               exit;
  5611.             end
  5612.         else
  5613.           if not (doIncludeDelimiter in LeftDelimiterOptions) then
  5614.             Inc (I);
  5615.  
  5616.         J := PosEx (RightDelimiter, S, RightFindOptions, I, StopIndex, RightCount);
  5617.         if J = 0 then
  5618.           if (RightCount <= 0) or (doOptional in RightDelimiterOptions) then
  5619.             J := StopIndex else
  5620.             begin
  5621.               Result := '';
  5622.               exit;
  5623.             end
  5624.         else
  5625.           if not (doIncludeDelimiter in RightDelimiterOptions) then
  5626.             Dec (J);
  5627.  
  5628.         if NotRange then
  5629.           begin
  5630.             Result := CopyRange (S, StartIndex, StopIndex);
  5631.             Delete (Result, I - StartIndex + 1, J - I + 1);
  5632.           end else
  5633.           Result := CopyRange (S, I, J);
  5634.       end;
  5635.   End;
  5636.  
  5637. Function DelimiterOptions (const Optional, IncludeDelimiter : Boolean) : TDelimiterOptions;
  5638.   Begin
  5639.     if Optional then
  5640.       Result := [doOptional] else
  5641.       Result := [];
  5642.     if IncludeDelimiter then
  5643.       Include (Result, doIncludeDelimiter);
  5644.   End;
  5645.  
  5646. Function CopyFrom (const S, Delimiter : String; const DelimiterOptional : Boolean; const FindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const Count : Integer) : String;
  5647.   Begin
  5648.     Result := CopyRight (S, Delimiter, DelimiterOptions (DelimiterOptional, True), FindOptions, Start, Stop, Count);
  5649.   End;
  5650.  
  5651. Function CopyFrom (const S : String; const Delimiter : CharSet; const DelimiterOptional : Boolean; const FindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const Count : Integer) : String;
  5652.   Begin
  5653.     Result := CopyRight (S, Delimiter, DelimiterOptions (DelimiterOptional, True), FindOptions, Start, Stop, Count);
  5654.   End;
  5655.  
  5656. Function CopyAfter (const S, Delimiter : String; const DelimiterOptional : Boolean; const FindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const Count : Integer) : String;
  5657.   Begin
  5658.     Result := CopyRight (S, Delimiter, DelimiterOptions (DelimiterOptional, False), FindOptions, Start, Stop, Count);
  5659.   End;
  5660.  
  5661. Function CopyAfter (const S : String; const Delimiter : CharSet; const DelimiterOptional : Boolean; const FindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const Count : Integer) : String;
  5662.   Begin
  5663.     Result := CopyRight (S, Delimiter, DelimiterOptions (DelimiterOptional, False), FindOptions, Start, Stop, Count);
  5664.   End;
  5665.  
  5666. Function CopyTo (const S, Delimiter : String; const DelimiterOptional : Boolean; const FindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const Count : Integer) : String;
  5667.   Begin
  5668.     Result := CopyLeft (S, Delimiter, DelimiterOptions (DelimiterOptional, True), FindOptions, Start, Stop, Count);
  5669.   End;
  5670.  
  5671. Function CopyTo (const S : String; const Delimiter : CharSet; const DelimiterOptional : Boolean; const FindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const Count : Integer) : String;
  5672.   Begin
  5673.     Result := CopyLeft (S, Delimiter, DelimiterOptions (DelimiterOptional, True), FindOptions, Start, Stop, Count);
  5674.   End;
  5675.  
  5676. Function CopyBefore (const S, Delimiter : String; const DelimiterOptional : Boolean; const FindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const Count : Integer) : String;
  5677.   Begin
  5678.     Result := CopyLeft (S, Delimiter, DelimiterOptions (DelimiterOptional, False), FindOptions, Start, Stop, Count);
  5679.   End;
  5680.  
  5681. Function CopyBefore (const S : String; const Delimiter : CharSet; const DelimiterOptional : Boolean; const FindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const Count : Integer) : String;
  5682.   Begin
  5683.     Result := CopyLeft (S, Delimiter, DelimiterOptions (DelimiterOptional, False), FindOptions, Start, Stop, Count);
  5684.   End;
  5685.  
  5686. Function CopyBetween (const S, LeftDelimiter, RightDelimiter : String; const LeftDelimiterOptional : Boolean; const RightDelimiterOptional : Boolean; const LeftFindOptions : TFindOptions; const RightFindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const LeftCount : Integer; const RightCount : Integer) : String;
  5687.   Begin
  5688.     Result := CopyRange (S, LeftDelimiter, RightDelimiter, DelimiterOptions (LeftDelimiterOptional, False), DelimiterOptions (RightDelimiterOptional, False), False, LeftFindOptions, RightFindOptions, Start, Stop, LeftCount, RightCount);
  5689.   End;
  5690.  
  5691. Function CopyBetween (const S, LeftDelimiter : String; const RightDelimiter : CharSet; const LeftDelimiterOptional : Boolean; const RightDelimiterOptional : Boolean; const LeftFindOptions : TFindOptions; const RightFindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const LeftCount : Integer; const RightCount : Integer) : String;
  5692.   Begin
  5693.     Result := CopyRange (S, LeftDelimiter, RightDelimiter, DelimiterOptions (LeftDelimiterOptional, False), DelimiterOptions (RightDelimiterOptional, False), False, LeftFindOptions, RightFindOptions, Start, Stop, LeftCount, RightCount);
  5694.   End;
  5695.  
  5696. Function CopyBetween (const S : String; const LeftDelimiter, RightDelimiter : CharSet; const LeftDelimiterOptional : Boolean; const RightDelimiterOptional : Boolean; const LeftFindOptions : TFindOptions; const RightFindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const LeftCount : Integer; const RightCount : Integer) : String;
  5697.   Begin
  5698.     Result := CopyRange (S, LeftDelimiter, RightDelimiter, DelimiterOptions (LeftDelimiterOptional, False), DelimiterOptions (RightDelimiterOptional, False), False, LeftFindOptions, RightFindOptions, Start, Stop, LeftCount, RightCount);
  5699.   End;
  5700.  
  5701. Function CopyBetween (const S : String; const LeftDelimiter : CharSet; const RightDelimiter : String; const LeftDelimiterOptional : Boolean; const RightDelimiterOptional : Boolean; const LeftFindOptions : TFindOptions; const RightFindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const LeftCount : Integer; const RightCount : Integer) : String;
  5702.   Begin
  5703.     Result := CopyRange (S, LeftDelimiter, RightDelimiter, DelimiterOptions (LeftDelimiterOptional, False), DelimiterOptions (RightDelimiterOptional, False), False, LeftFindOptions, RightFindOptions, Start, Stop, LeftCount, RightCount);
  5704.   End;
  5705.  
  5706. Function RemoveBetween (const S, LeftDelimiter, RightDelimiter : String; const LeftDelimiterOptional : Boolean; const RightDelimiterOptional : Boolean; const LeftFindOptions : TFindOptions; const RightFindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const LeftCount : Integer; const RightCount : Integer) : String;
  5707.   Begin
  5708.     Result := CopyRange (S, LeftDelimiter, RightDelimiter, DelimiterOptions (LeftDelimiterOptional, True), DelimiterOptions (RightDelimiterOptional, True), True, LeftFindOptions, RightFindOptions, Start, Stop, LeftCount, RightCount);
  5709.   End;
  5710.  
  5711. Function RemoveBetween (const S, LeftDelimiter : String; const RightDelimiter : CharSet; const LeftDelimiterOptional : Boolean; const RightDelimiterOptional : Boolean; const LeftFindOptions : TFindOptions; const RightFindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const LeftCount : Integer; const RightCount : Integer) : String;
  5712.   Begin
  5713.     Result := CopyRange (S, LeftDelimiter, RightDelimiter, DelimiterOptions (LeftDelimiterOptional, True), DelimiterOptions (RightDelimiterOptional, True), True, LeftFindOptions, RightFindOptions, Start, Stop, LeftCount, RightCount);
  5714.   End;
  5715.  
  5716. Function RemoveBetween (const S : String; const LeftDelimiter, RightDelimiter : CharSet; const LeftDelimiterOptional : Boolean; const RightDelimiterOptional : Boolean; const LeftFindOptions : TFindOptions; const RightFindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const LeftCount : Integer; const RightCount : Integer) : String;
  5717.   Begin
  5718.     Result := CopyRange (S, LeftDelimiter, RightDelimiter, DelimiterOptions (LeftDelimiterOptional, True), DelimiterOptions (RightDelimiterOptional, True), True, LeftFindOptions, RightFindOptions, Start, Stop, LeftCount, RightCount);
  5719.   End;
  5720.  
  5721. Function RemoveBetween (const S : String; const LeftDelimiter : CharSet; const RightDelimiter : String; const LeftDelimiterOptional : Boolean; const RightDelimiterOptional : Boolean; const LeftFindOptions : TFindOptions; const RightFindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const LeftCount : Integer; const RightCount : Integer) : String;
  5722.   Begin
  5723.     Result := CopyRange (S, LeftDelimiter, RightDelimiter, DelimiterOptions (LeftDelimiterOptional, True), DelimiterOptions (RightDelimiterOptional, True), True, LeftFindOptions, RightFindOptions, Start, Stop, LeftCount, RightCount);
  5724.   End;
  5725.  
  5726. Function Remove (const S, LeftDelimiter, RightDelimiter : String; const LeftDelimiterOptional : Boolean; const RightDelimiterOptional : Boolean; const LeftFindOptions : TFindOptions; const RightFindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const LeftCount : Integer; const RightCount : Integer) : String;
  5727.   Begin
  5728.     Result := CopyRange (S, LeftDelimiter, RightDelimiter, DelimiterOptions (LeftDelimiterOptional, False), DelimiterOptions (RightDelimiterOptional, False), True, LeftFindOptions, RightFindOptions, Start, Stop, LeftCount, RightCount);
  5729.   End;
  5730.  
  5731. Function Remove (const S, LeftDelimiter : String; const RightDelimiter : CharSet; const LeftDelimiterOptional : Boolean; const RightDelimiterOptional : Boolean; const LeftFindOptions : TFindOptions; const RightFindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const LeftCount : Integer; const RightCount : Integer) : String;
  5732.   Begin
  5733.     Result := CopyRange (S, LeftDelimiter, RightDelimiter, DelimiterOptions (LeftDelimiterOptional, False), DelimiterOptions (RightDelimiterOptional, False), True, LeftFindOptions, RightFindOptions, Start, Stop, LeftCount, RightCount);
  5734.   End;
  5735.  
  5736. Function Remove (const S : String; const LeftDelimiter, RightDelimiter : CharSet; const LeftDelimiterOptional : Boolean; const RightDelimiterOptional : Boolean; const LeftFindOptions : TFindOptions; const RightFindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const LeftCount : Integer; const RightCount : Integer) : String;
  5737.   Begin
  5738.     Result := CopyRange (S, LeftDelimiter, RightDelimiter, DelimiterOptions (LeftDelimiterOptional, False), DelimiterOptions (RightDelimiterOptional, False), True, LeftFindOptions, RightFindOptions, Start, Stop, LeftCount, RightCount);
  5739.   End;
  5740.  
  5741. Function Remove (const S : String; const LeftDelimiter : CharSet; const RightDelimiter : String; const LeftDelimiterOptional : Boolean; const RightDelimiterOptional : Boolean; const LeftFindOptions : TFindOptions; const RightFindOptions : TFindOptions; const Start : Integer; const Stop : Integer; const LeftCount : Integer; const RightCount : Integer) : String;
  5742.   Begin
  5743.     Result := CopyRange (S, LeftDelimiter, RightDelimiter, DelimiterOptions (LeftDelimiterOptional, False), DelimiterOptions (RightDelimiterOptional, False), True, LeftFindOptions, RightFindOptions, Start, Stop, LeftCount, RightCount);
  5744.   End;
  5745.  
  5746.  
  5747.  
  5748. {                                                                              }
  5749. { Quoting and Escaping                                                         }
  5750. {                                                                              }
  5751. Function EscapeText (const S : String; const CharsToEscape : Array of Char;
  5752.           const EscapePrefix : Char; const EscapeChar : Array of Char) : String;
  5753. var I, J : Integer;
  5754.     Iterator : TFindItemIterator;
  5755.   Begin
  5756.     Result := '';
  5757.     J := 1;
  5758.     I := FindFirstPos (Iterator, CharsToEscape, S);
  5759.     While I > 0 do
  5760.       begin
  5761.         Result := Result + CopyRange (S, J, I - 1) + EscapePrefix + EscapeChar [Iterator.ItemIndex];
  5762.         J := I + 1;
  5763.         I := FindNextPos (Iterator, CharsToEscape);
  5764.       end;
  5765.     if J = 1 then
  5766.       Result := S else
  5767.       Result := Result + CopyFrom (S, J);
  5768.   End;
  5769.  
  5770. Function HexEscapeText (const S : String; const CharsToEscape : CharSet;
  5771.          const EscapePrefix : String; const EscapePostfix : String;
  5772.          const UpperHex : Boolean; const AlwaysTwoDigits : Boolean) : String;
  5773. var I, J     : Integer;
  5774.     Iterator : TFindCharSetIterator;
  5775.     H        : String;
  5776.   Begin
  5777.     Result := '';
  5778.     J := 1;
  5779.     I := FindFirstPos (Iterator, CharsToEscape, S);
  5780.     While I > 0 do
  5781.       begin
  5782.         if AlwaysTwoDigits then
  5783.           H := LongWordToHex (Ord (S [I]), 2) else
  5784.           H := LongWordToHex (Ord (S [I]), 1);
  5785.         if UpperHex then
  5786.           ConvertUpper (H) else
  5787.           ConvertLower (H);
  5788.         Result := Result + CopyRange (S, J, I - 1) +
  5789.                   EscapePrefix + H + EscapePostfix;
  5790.         J := I + 1;
  5791.         I := FindNextPos (Iterator);
  5792.       end;
  5793.     if J = 1 then
  5794.       Result := S else
  5795.       Result := Result + CopyFrom (S, J);
  5796.   End;
  5797.  
  5798. Function HexUnescapeText (const S : String; const EscapePrefix : Char) : String;
  5799. var I, J : Integer;
  5800.   Begin
  5801.     Result := '';
  5802.     J := 1;
  5803.     Repeat
  5804.       I := Pos (EscapePrefix, S, [], J);
  5805.       if I > 0 then
  5806.         begin
  5807.           Result := Result + CopyRange (S, J + 1, I - 1);
  5808.           if I < Length (S) - 1 then
  5809.             begin
  5810.               Result := Result + Char (StrToIntDef ('$' + Copy (S, I + 1, 2), 32));
  5811.               J := I + 3;
  5812.             end else
  5813.             J := I + 1;
  5814.         end;
  5815.     Until I = 0;
  5816.     if (I = 0) and (J = 0) then
  5817.       Result := S else
  5818.       Result := Result + CopyFrom (S, J + 1);
  5819.   End;
  5820.  
  5821. Function UnescapeText (const S : String; const EscapePrefix : Char;
  5822.          const EscapeChar : Array of Char; const Replacement : Array of String;
  5823.          const AlwaysDropPrefix : Boolean) : String;
  5824. var I, J : Integer;
  5825.     F, G : Integer;
  5826.     Ch   : Char;
  5827.     T    : String;
  5828.   Begin
  5829.     Assert (High (EscapeChar) = High (Replacement), 'Arrays must be of equal length');
  5830.  
  5831.     Result := '';
  5832.     J := 1;
  5833.     Repeat
  5834.       I := Pos (EscapePrefix, S, [], J);
  5835.       if I > 0 then
  5836.         begin
  5837.           G := -1;
  5838.           if I < Length (S) then
  5839.             begin
  5840.               Ch := S [I + 1];
  5841.               For F := 0 to High (EscapeChar) do
  5842.                 if EscapeChar [F] = Ch then
  5843.                   begin
  5844.                     G := F;
  5845.                     break;
  5846.                   end;
  5847.             end;
  5848.           Result := Result + CopyRange (S, J + 1, I - 1);
  5849.           if G >= 0 then
  5850.             begin
  5851.               T := Replacement [G];
  5852.               Result := Result + T;
  5853.               J := I + Length (T);
  5854.             end else
  5855.             begin
  5856.               if not AlwaysDropPrefix then
  5857.                 Result := Result + EscapePrefix;
  5858.               J := I + 1;
  5859.             end;
  5860.         end;
  5861.     Until I = 0;
  5862.     if (I = 0) and (J = 0) then
  5863.       Result := S else
  5864.       Result := Result + CopyFrom (S, J + 1);
  5865.   End;
  5866.  
  5867. Function CEscapeText (const S : String) : String;
  5868.   Begin
  5869.     Result := EscapeText (S, [#13, #10, #0, #7, #27, '\'], '\', ['n', 'l', '0', 'b', 'e', '\']);
  5870.   End;
  5871.  
  5872. Function CUnescapeText (const S : String) : String;
  5873.   Begin
  5874.     Result := UnescapeText (S, '\', ['n', 'l', '0', 'b', 'e', '\'], [#13, #10, #0, #7, #27, '\']);
  5875.   End;
  5876.  
  5877. Function QuoteText (const S : String; const Quotes : Char) : String;
  5878.   Begin
  5879.     Result := Quotes + Replace (Quotes, Quotes + Quotes, S) + Quotes;
  5880.   End;
  5881.  
  5882. Function UnquoteText (const S : String) : String;
  5883. var Quote : Char;
  5884.     L     : Integer;
  5885.   Begin
  5886.     L := Length (S);
  5887.     if (L < 2) or (S [1] <> S [L]) then
  5888.       begin
  5889.         Result := S;
  5890.         exit;
  5891.       end;
  5892.     Quote := S [1];
  5893.     Result := Replace (Quote + Quote, Quote, S, [], 2, L - 1);
  5894.   End;
  5895.  
  5896. Function FindClosingQuote (const S : String; const OpenQuotePos : Integer) : Integer;
  5897. var I : Integer;
  5898.     OpenQuote : Char;
  5899.     R : Boolean;
  5900.   Begin
  5901.     if (OpenQuotePos <= 0) or (OpenQuotePos > Length (S)) then
  5902.       begin
  5903.         Result := 0;
  5904.         exit;
  5905.       end;
  5906.     I := OpenQuotePos;
  5907.     OpenQuote := S [I];
  5908.     Repeat
  5909.       I := Pos (OpenQuote, S, [], I + 1);
  5910.       if I = 0 then
  5911.         begin
  5912.           Result := 0;
  5913.           exit;
  5914.         end;
  5915.       R := (I = Length (S)) or (S [I + 1] <> OpenQuote);
  5916.       if not R then
  5917.         Inc (I);
  5918.     Until R;
  5919.     Result := I;
  5920.   End;
  5921.  
  5922. Function RemoveQuotes (const S : String; const Quotes : Char = '''') : String;
  5923. var L : Integer;
  5924.   Begin
  5925.     L := Length (S);
  5926.     if (L >= 2) and (S [1] = Quotes) and (S [L] = Quotes) then
  5927.       Result := CopyRange (S, 2, L - 1) else
  5928.       Result := S;
  5929.   End;
  5930.  
  5931. Function EncodeDotLineTerminated (const S : String) : String;
  5932.   Begin
  5933.     Result := S;
  5934.     if (Length (Result) >= 1) and (Result [1] = '.') then
  5935.       Insert ('.', Result, 1);
  5936.     Result := Replace (CRLF + '.', CRLF + '..', Result) +
  5937.               '.' + CRLF;
  5938.   End;
  5939.  
  5940. Function DecodeDotLineTerminated (const S : String) : String;
  5941.   Begin
  5942.     if not MatchRight ('.' + CRLF, S) then
  5943.       raise EConvertError.Create ('Not dot line terminated');
  5944.     Result := Replace (CRLF + '.', CRLF, S);
  5945.     Delete (Result, Length (Result) - 1, 2);
  5946.     if (Length (Result) >= 1) and (Result [1] = '.') then
  5947.       Delete (Result, 1, 1);
  5948.   End;
  5949.  
  5950. Function EncodeEmptyLineTerminated (const S : String) : String;
  5951.   Begin
  5952.     Result := WithSuffix (S, CRLF);
  5953.     if (Length (Result) >= 2) and (Result [1] = ASCII_CR) and (Result [2] = ASCII_LF) then
  5954.       Insert ('.', Result, 1);
  5955.     Result := Replace (CRLF + CRLF, CRLF + '.' + CRLF, Result) +
  5956.               CRLF;
  5957.   End;
  5958.  
  5959. Function DecodeEmptyLineTerminated (const S : String) : String;
  5960.   Begin
  5961.     if not MatchRight (CRLF, S) then
  5962.       raise EConvertError.Create ('Not dot line terminated');
  5963.     Result := Replace (CRLF + '.', CRLF, CopyLeft (S, Length (S) - 2));
  5964.     if (Length (Result) >= 1) and (Result [1] = '.') then
  5965.       Delete (Result, 1, 1);
  5966.   End;
  5967.  
  5968. Function SplitQuotedList (const S : String; const Delimiter : String; const Quotes : CharSet) : StringArray;
  5969. var I, J : Integer;
  5970.   Begin
  5971.     I := 1;
  5972.     While I <= Length (S) do
  5973.       begin
  5974.         J := MatchQuotedString (S, Quotes, I);
  5975.         if J > 0 then
  5976.           begin
  5977.             Append (Result, UnquoteText (CopyRange (S, I, I + J - 1)));
  5978.             Inc (I, J);
  5979.             if Match (Delimiter, S, I) then
  5980.               Inc (I, Length (Delimiter));
  5981.           end else
  5982.           begin
  5983.             J := Pos (Delimiter, S, [], I);
  5984.             if J > 0 then
  5985.               begin
  5986.                 Append (Result, CopyRange (S, I, J - 1));
  5987.                 I := J + Length (Delimiter);
  5988.               end else
  5989.               begin
  5990.                 Append (Result, CopyFrom (S, I));
  5991.                 exit;
  5992.               end;
  5993.           end;
  5994.       end;
  5995.   End;
  5996.  
  5997.  
  5998.  
  5999. Function WithSuffix (const S : String; const Suffix : String; const CaseSensitive : Boolean) : String;
  6000.   Begin
  6001.     if not MatchRight (Suffix, S, CaseSensitive) then
  6002.       Result := S + Suffix else
  6003.       Result := S;
  6004.   End;
  6005.  
  6006. Function WithPrefix (const S : String; const Prefix : String; const CaseSensitive : Boolean) : String;
  6007.   Begin
  6008.     if not MatchLeft (Prefix, S, CaseSensitive) then
  6009.       Result := Prefix + S else
  6010.       Result := S;
  6011.   End;
  6012.  
  6013. Function WithoutSuffix (const S : String; const Suffix : String; const CaseSensitive : Boolean) : String;
  6014.   Begin
  6015.     if MatchRight (Suffix, S, CaseSensitive) then
  6016.       Result := Copy (S, 1, Length (S) - Length (Suffix)) else
  6017.       Result := S;
  6018.   End;
  6019.  
  6020. Function WithoutPrefix (const S : String; const Prefix : String; const CaseSensitive : Boolean) : String;
  6021.   Begin
  6022.     if MatchLeft (Prefix, S, CaseSensitive) then
  6023.       Result := CopyFrom (S, Length (Prefix) + 1) else
  6024.       Result := S;
  6025.   End;
  6026.  
  6027. Procedure EnsureSuffix (var S : String; const Suffix : String; const CaseSensitive : Boolean);
  6028. var L, M : Integer;
  6029.     P : PChar;
  6030.   Begin
  6031.     if (Suffix <> '') and not MatchRight (Suffix, S, CaseSensitive) then
  6032.       begin
  6033.         L := Length (S);
  6034.         M := Length (Suffix);
  6035.         SetLength (S, L + M);
  6036.         P := Pointer (S);
  6037.         Inc (P, L);
  6038.         MoveMem (Pointer (Suffix)^, P^, M);
  6039.       end;
  6040.   End;
  6041.  
  6042. Procedure EnsurePrefix (var S : String; const Prefix : String; const CaseSensitive : Boolean);
  6043. var L, M : Integer;
  6044.     P : PChar;
  6045.   Begin
  6046.     if (Prefix <> '') and not MatchLeft (Prefix, S, CaseSensitive) then
  6047.       begin
  6048.         L := Length (S);
  6049.         M := Length (Prefix);
  6050.         SetLength (S, L + M);
  6051.         if L > 0 then
  6052.           begin
  6053.             P := Pointer (S);
  6054.             Inc (P, M);
  6055.             MoveMem (Pointer (S)^, P^, L);
  6056.           end;
  6057.         MoveMem (Pointer (Prefix)^, Pointer (S)^, M);
  6058.       end;
  6059.   End;
  6060.  
  6061. Procedure EnsureNoSuffix (var S : String; const Suffix : String; const CaseSensitive : Boolean);
  6062.   Begin
  6063.     if MatchRight (Suffix, S, CaseSensitive) then
  6064.       SetLength (S, Length (S) - Length (Suffix));
  6065.   End;
  6066.  
  6067. Procedure EnsureNoPrefix (var S : String; const Prefix : String; const CaseSensitive : Boolean);
  6068. var L, M : Integer;
  6069.     P : PChar;
  6070.   Begin
  6071.     if MatchLeft (Prefix, S, CaseSensitive) then
  6072.       begin
  6073.         L := Length (S);
  6074.         M := Length (Prefix);
  6075.         P := Pointer (S);
  6076.         Inc (P, M);
  6077.         MoveMem (P^, Pointer (S)^, L - M);
  6078.         SetLength (S, L - M);
  6079.       end;
  6080.   End;
  6081.  
  6082. Procedure SetLengthAndZero (var S : String; const NewLength : Integer);
  6083. var L : Integer;
  6084.     P : PChar;
  6085.   Begin
  6086.     L := Length (S);
  6087.     if L = NewLength then
  6088.       exit;
  6089.     SetLength (S, NewLength);
  6090.     if L > NewLength then
  6091.       exit;
  6092.     P := Pointer (S);
  6093.     Inc (P, L);
  6094.     FillChar (P^, NewLength - L, #0);
  6095.   End;
  6096.  
  6097.  
  6098. {                                                                              }
  6099. { Natural language                                                             }
  6100. {   US style billion = 1,000 million                                           }
  6101. {   UK style billion = 1,000,000 million                                       }
  6102. {                                                                              }
  6103. Function Number (const Num : Int64; const USStyle : Boolean = False) : String;
  6104. var I : Int64;
  6105. const
  6106.   Eng_minus    = 'minus ';
  6107.   Eng_Numbers  : Array [0..12] of String =
  6108.         ('zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine',
  6109.          'ten', 'eleven', 'twelve');
  6110.   Eng_Prefixes : Array [2..9] of String =
  6111.         ('twen', 'thir', 'four', 'fif', 'six', 'seven', 'eigh', 'nin');
  6112.   Eng_teen     = 'teen';
  6113.   Eng_ty       = 'ty';
  6114.   Eng_hundred  = ' hundred';
  6115.   Eng_and      = ' and ';
  6116.   Eng_thousand  = ' thousand';
  6117.   Eng_million   = ' million';
  6118.   Eng_billion   = ' billion';
  6119.   USBillion  : Int64 = 1000000000;
  6120.   UKBillion  : Int64 = 1000000000000;
  6121.  
  6122.   Begin
  6123.     if Num < 0 then
  6124.       Result := Eng_minus + Number (-Num) else
  6125.     if Num <= 12 then
  6126.       Result := Eng_Numbers [Num] else
  6127.     if Num <= 19 then
  6128.       Result := Eng_Prefixes [Num mod 10] + Eng_teen else
  6129.     if Num <= 99 then
  6130.       begin
  6131.         Result := Eng_Prefixes [Num div 10] + Eng_ty;
  6132.         if Num mod 10 > 0 then
  6133.           Result := Result + ' ' + Eng_Numbers [Num mod 10];
  6134.       end else
  6135.     if Num <= 999 then
  6136.       begin
  6137.         Result := Number (Num div 100) + Eng_hundred;
  6138.         if Num mod 100 > 0 then
  6139.           Result := Result + Eng_and + Number (Num mod 100);
  6140.       end else
  6141.     if Num <= 999999 then
  6142.       begin
  6143.         Result := Number (Num div 1000) + Eng_thousand;
  6144.         if Num mod 1000 > 0 then
  6145.           Result := Result + ' ' + Number (Num mod 1000);
  6146.       end else
  6147.     if ((Num < USBillion) and USStyle) or
  6148.        ((Num < UKBillion) and not USStyle) then
  6149.       begin
  6150.         Result := Number (Num div 1000000) + Eng_million;
  6151.         if Num mod 1000000 > 0 then
  6152.           Result := Result + ' ' + Number (Num mod 1000000);
  6153.       end else
  6154.       begin
  6155.         if USStyle then
  6156.           I := USBillion else
  6157.           I := UKBillion;
  6158.         Result := Number (Num div I) + Eng_billion;
  6159.         if Num mod I > 0 then
  6160.           Result := Result + ' ' + Number (Num mod I, USStyle);
  6161.       end;
  6162.   End;
  6163.  
  6164. Function Number (const Num : Extended; const USStyle : Boolean = False) : String;
  6165. const Eng_point = ' point';
  6166. var N, I : Int64;
  6167.   Begin
  6168.     Result := Number (Trunc (Num), USStyle);
  6169.     N := Abs (Round (Frac (Num) * 1000000));
  6170.     if N > 0 then
  6171.       begin
  6172.         Result := Result + Eng_point;
  6173.         I := 100000;
  6174.         While (I > 1) and (N > 0) do
  6175.           begin
  6176.             Result := Result + ' ' + Number (N div I);
  6177.             N := N mod I;
  6178.             I := I div 10;
  6179.           end;
  6180.       end;
  6181.   End;
  6182.  
  6183. Function StorageSize (const Bytes : Int64; const ShortFormat : Boolean) : String;
  6184. var Size, Suffix : String;
  6185.     Fmt          : String;
  6186.   Begin
  6187.     Fmt := iif (ShortFormat, '%1.0f', '%0.1f');
  6188.     if Bytes < 1024 then
  6189.       begin
  6190.         Size := IntToStr (Bytes);
  6191.         Suffix := iif (ShortFormat, 'b', 'bytes');
  6192.       end else
  6193.     if Bytes < 1024 * 1024 then
  6194.       begin
  6195.         Size := Format (Fmt, [Bytes / 1024]);
  6196.         Suffix := iif (ShortFormat, 'K', 'Kb');
  6197.       end else
  6198.     if Bytes < 1024 * 1024 * 1024 then
  6199.       begin
  6200.         Size := Format (Fmt, [Bytes / (1024 * 1024)]);
  6201.         Suffix := iif (ShortFormat, 'M', 'Mb');
  6202.       end else
  6203.     if Bytes < Int64 (1024) * 1024 * 1024 * 1024 then
  6204.       begin
  6205.         Size := Format (Fmt, [Bytes / (1024 * 1024 * 1024)]);
  6206.         Suffix := iif (ShortFormat, 'G', 'Gb');
  6207.       end else
  6208.       begin
  6209.         Size := Format (Fmt, [Bytes / (Int64 (1024) * 1024 * 1024 * 1024)]);
  6210.         Suffix := iif (ShortFormat, 'T', 'Tb');
  6211.       end;
  6212.     if Match ('.0', Size, Length (Size) - 1) then
  6213.       SetLength (Size, Length (Size) - 2);
  6214.     Result := Size + ' ' + Suffix;
  6215.   End;
  6216.  
  6217. Function TransferRate (const Bytes, MillisecondsElapsed : Int64; const ShortFormat : Boolean) : String;
  6218.   Begin
  6219.     if MillisecondsElapsed <= 0 then
  6220.       Result := '' else
  6221.       Result := StorageSize (Trunc (Bytes / (MillisecondsElapsed / 1000.0)), ShortFormat) + '/s';
  6222.   End;
  6223.  
  6224.  
  6225.  
  6226. {                                                                              }
  6227. { Pack                                                                         }
  6228. {                                                                              }
  6229. Function Pack (const D : Int64) : String;
  6230.   Begin
  6231.     SetLength (Result, Sizeof (D));
  6232.     Move (D, Pointer (Result)^, Sizeof (D));
  6233.   End;
  6234.  
  6235. Function Pack (const D : Integer) : String;
  6236.   Begin
  6237.     SetLength (Result, Sizeof (D));
  6238.     PInteger (Result)^ := D;
  6239.   End;
  6240.  
  6241. Function Pack (const D : SmallInt) : String;
  6242.   Begin
  6243.     SetLength (Result, Sizeof (D));
  6244.     PSmallInt (Result)^ := D;
  6245.   End;
  6246.  
  6247. Function Pack (const D : ShortInt) : String;
  6248.   Begin
  6249.     SetLength (Result, Sizeof (D));
  6250.     PShortInt (Result)^ := D;
  6251.   End;
  6252.  
  6253. Function Pack (const D : Byte) : String;
  6254.   Begin
  6255.     Result := Char (D);
  6256.   End;
  6257.  
  6258. Function Pack (const D : Word) : String;
  6259.   Begin
  6260.     Result := Char (Lo (D)) + Char (Hi (D));
  6261.   End;
  6262.  
  6263. Function Pack (const D : String) : String;
  6264.   Begin
  6265.     Result := Pack (Length (D)) + D;
  6266.   End;
  6267.  
  6268. Function PackShortString (const D : ShortString) : String;
  6269.   Begin
  6270.     Result := D [0] + D;
  6271.   End;
  6272.  
  6273. Function PackSingle (const D : Single) : String;
  6274.   Begin
  6275.     SetLength (Result, Sizeof (D));
  6276.     Move (D, Pointer (Result)^, Sizeof (D));
  6277.   End;
  6278.  
  6279. Function PackDouble (const D : Double) : String;
  6280.   Begin
  6281.     SetLength (Result, Sizeof (D));
  6282.     Move (D, Pointer (Result)^, Sizeof (D));
  6283.   End;
  6284.  
  6285. Function Pack (const D : Extended) : String;
  6286.   Begin
  6287.     SetLength (Result, Sizeof (D));
  6288.     Move (D, Pointer (Result)^, Sizeof (D));
  6289.   End;
  6290.  
  6291. Function PackCurrency (const D : Currency) : String;
  6292.   Begin
  6293.     SetLength (Result, Sizeof (D));
  6294.     Move (D, Pointer (Result)^, Sizeof (D));
  6295.   End;
  6296.  
  6297. Function PackDateTime (const D : TDateTime) : String;
  6298.   Begin
  6299.     SetLength (Result, Sizeof (D));
  6300.     Move (D, Pointer (Result)^, Sizeof (D));
  6301.   End;
  6302.  
  6303. Function Pack (const D : Boolean) : String;
  6304.   Begin
  6305.     Result := Char (Ord (D));
  6306.   End;
  6307.  
  6308. Function UnpackShortString (const D : String) : ShortString;
  6309. var L : Byte;
  6310.   Begin
  6311.     Assert (Length (D) > 0, 'Invalid argument: String too short');
  6312.     L := Byte (D [1]);
  6313.     Assert (Length (D) >= L + 1, 'Invalid argument: String too short');
  6314.     SetLength (Result, L);
  6315.     if L > 0 then
  6316.       Move (D [2], Result [1], L);
  6317.   End;
  6318.  
  6319. Function UnpackString (const D : String) : String;
  6320. var L : Integer;
  6321.   Begin
  6322.     L := UnpackInteger (CopyLeft (D, Sizeof (Integer)));
  6323.     Assert (Length (D) >= Sizeof (Integer) + L, 'Invalid argument: String too short');
  6324.     SetLength (Result, L);
  6325.     if L > 0 then
  6326.       Move (D [5], Result [1], L);
  6327.   End;
  6328.  
  6329. Function UnpackInteger (const D : String) : Integer;
  6330.   Begin
  6331.     Assert (Length (D) >= Sizeof (Result), 'Invalid argument: String too short');
  6332.     Result := PInteger (D)^;
  6333.   End;
  6334.  
  6335. Function UnpackSingle (const D : String) : Single;
  6336.   Begin
  6337.     Assert (Length (D) >= Sizeof (Result), 'Invalid argument: String too short');
  6338.     Move (Pointer (D)^, Result, Sizeof (Result));
  6339.   End;
  6340.  
  6341. Function UnpackDouble (const D : String) : Double;
  6342.   Begin
  6343.     Assert (Length (D) >= Sizeof (Result), 'Invalid argument: String too short');
  6344.     Move (Pointer (D)^, Result, Sizeof (Result));
  6345.   End;
  6346.  
  6347. Function UnpackExtended (const D : String) : Extended;
  6348.   Begin
  6349.     Assert (Length (D) >= Sizeof (Result), 'Invalid argument: String too short');
  6350.     Move (Pointer (D)^, Result, Sizeof (Result));
  6351.   End;
  6352.  
  6353. Function UnpackBoolean (const D : String) : Boolean;
  6354.   Begin
  6355.     Assert (Length (D) >= Sizeof (Result), 'Invalid argument: String too short');
  6356.     Result := PBoolean (D)^;
  6357.   End;
  6358.  
  6359. Function UnpackDateTime (const D : String) : TDateTime;
  6360.   Begin
  6361.     Assert (Length (D) >= Sizeof (Result), 'Invalid argument: String too short');
  6362.     Move (Pointer (D)^, Result, Sizeof (Result));
  6363.   End;
  6364.  
  6365.  
  6366.  
  6367.  
  6368. {                                                                              }
  6369. { PChar routines                                                               }
  6370. {                                                                              }
  6371. Function SkipChar (var P : PChar; const C : Char) : Boolean;
  6372. var Q : PChar;
  6373.     D : Char;
  6374.   Begin
  6375.     Assert (C <> #0, 'Invalid parameter');
  6376.     Q := P;
  6377.     if not Assigned (Q) then
  6378.       Result := False else
  6379.       begin
  6380.         D := Q^;
  6381.         if D = #0 then
  6382.           Result := False else
  6383.           if D = C then
  6384.             begin
  6385.               Inc (P);
  6386.               Result := True;
  6387.             end else
  6388.             Result := False;
  6389.       end;
  6390.   End;
  6391.  
  6392. Function SkipChar (var P : PChar; const C : CharSet) : Boolean;
  6393. var Q : PChar;
  6394.     D : Char;
  6395.   Begin
  6396.     Q := P;
  6397.     if not Assigned (Q) then
  6398.       Result := False else
  6399.       begin
  6400.         D := Q^;
  6401.         if D = #0 then
  6402.           Result := False else
  6403.           if D in C then
  6404.             begin
  6405.               Inc (P);
  6406.               Result := True;
  6407.             end else
  6408.             Result := False;
  6409.       end;
  6410.   End;
  6411.  
  6412. Function SkipAll (var P : PChar; const C : Char) : Integer;
  6413. var Q : PChar;
  6414.   Begin
  6415.     Assert (C <> #0, 'Invalid parameter');
  6416.     Result := 0;
  6417.     Q := P;
  6418.     if not Assigned (Q) then
  6419.       exit;
  6420.     While (Q^ <> #0) and (Q^ = C) do
  6421.       begin
  6422.         Inc (Q);
  6423.         Inc (Result);
  6424.       end;
  6425.     P := Q;
  6426.   End;
  6427.  
  6428. Function SkipAll (var P : PChar; const C : CharSet) : Integer;
  6429. var Q : PChar;
  6430.   Begin
  6431.     Result := 0;
  6432.     Q := P;
  6433.     if not Assigned (Q) then
  6434.       exit;
  6435.     While (Q^ <> #0) and (Q^ in C) do
  6436.       begin
  6437.         Inc (Q);
  6438.         Inc (Result);
  6439.       end;
  6440.     P := Q;
  6441.   End;
  6442.  
  6443. Function SkipSeq (var P : PChar; const S1, S2 : CharSet) : Boolean;
  6444. var Q : PChar;
  6445.     C : Char;
  6446.   Begin
  6447.     Q := P;
  6448.     if not Assigned (Q) then
  6449.       begin
  6450.         Result := False;
  6451.         exit;
  6452.       end;
  6453.     C := Q^;
  6454.     if (C = #0) or not (C in S1) then
  6455.       begin
  6456.         Result := False;
  6457.         exit;
  6458.       end;
  6459.     Inc (Q);
  6460.     C := Q^;
  6461.     if (C = #0) or not (C in S2) then
  6462.       Result := False else
  6463.       begin
  6464.         Inc (P, 2);
  6465.         Result := True;
  6466.       end;
  6467.   End;
  6468.  
  6469. Function SkipSeq (var P : PChar; const S1, S2, S3 : CharSet) : Boolean;
  6470. var Q : PChar;
  6471.     C : Char;
  6472.   Begin
  6473.     Q := P;
  6474.     if not Assigned (Q) then
  6475.       begin
  6476.         Result := False;
  6477.         exit;
  6478.       end;
  6479.     C := Q^;
  6480.     if (C = #0) or not (C in S1) then
  6481.       begin
  6482.         Result := False;
  6483.         exit;
  6484.       end;
  6485.     Inc (Q);
  6486.     C := Q^;
  6487.     if (C = #0) or not (C in S2) then
  6488.       begin
  6489.         Result := False;
  6490.         exit;
  6491.       end;
  6492.     Inc (Q);
  6493.     C := Q^;
  6494.     if (C = #0) or not (C in S3) then
  6495.       Result := False else
  6496.       begin
  6497.         Inc (P, 3);
  6498.         Result := True;
  6499.       end;
  6500.   End;
  6501.  
  6502. Function ExtractAll (var P : PChar; const C : Char) : String;
  6503. var Q : PChar;
  6504.     I : Integer;
  6505.   Begin
  6506.     Q := P;
  6507.     I := SkipAll (P, C);
  6508.     if I = 0 then
  6509.       begin
  6510.         Result := '';
  6511.         exit;
  6512.       end;
  6513.     SetLength (Result, I);
  6514.     MoveMem (Q^, Pointer (Result)^, I);
  6515.   End;
  6516.  
  6517. Function ExtractAll (var P : PChar; const C : CharSet) : String;
  6518. var Q : PChar;
  6519.     I : Integer;
  6520.   Begin
  6521.     Q := P;
  6522.     I := SkipAll (P, C);
  6523.     if I = 0 then
  6524.       begin
  6525.         Result := '';
  6526.         exit;
  6527.       end;
  6528.     SetLength (Result, I);
  6529.     MoveMem (Q^, Pointer (Result)^, I);
  6530.   End;
  6531.  
  6532. Function ExtractTo (var P : PChar; const C : CharSet) : String;
  6533. var S : CharSet;
  6534.   Begin
  6535.     S := C;
  6536.     ComplementCharSet (S);
  6537.     Result := ExtractAll (P, S);
  6538.   End;
  6539.  
  6540. Function MatchString (const P : PChar; const S : String; const CaseSensitive : Boolean) : Boolean;
  6541. var T, Q : PChar;
  6542.     I, L : Integer;
  6543.   Begin
  6544.     L := Length (S);
  6545.     if L = 0 then
  6546.       begin
  6547.         Result := False;
  6548.         exit;
  6549.       end;
  6550.     T := P;
  6551.     Q := Pointer (S);
  6552.     if CaseSensitive then
  6553.       begin
  6554.         For I := 1 to L do
  6555.           if (T^ = #0) or (T^ <> Q^) then
  6556.             begin
  6557.               Result := False;
  6558.               exit;
  6559.             end;
  6560.         Result := True;
  6561.       end else
  6562.       begin
  6563.         For I := 1 to L do
  6564.           if (T^ = #0) or not MatchNoCase (T^, Q^) then
  6565.             begin
  6566.               Result := False;
  6567.               exit;
  6568.             end;
  6569.         Result := True;
  6570.       end;
  6571.   End;
  6572.  
  6573. Function SkipString (var P : PChar; const S : String; const CaseSensitive : Boolean) : Boolean;
  6574.   Begin
  6575.     Result := MatchString (P, S, CaseSensitive);
  6576.     if Result then
  6577.       Inc (P, Length (S));
  6578.   End;
  6579.  
  6580. Function ExtractTo (var P : PChar; const S : String; const CaseSensitive : Boolean) : String;
  6581. var Q : PChar;
  6582.     L : Integer;
  6583.   Begin
  6584.     Q := P;
  6585.     L := 0;
  6586.     While (P^ <> #0) and not MatchString (P, S, CaseSensitive) do
  6587.       begin
  6588.         Inc (P);
  6589.         Inc (L);
  6590.       end;
  6591.     SetLength (Result, L);
  6592.     if L = 0 then
  6593.       exit;
  6594.     MoveMem (Q^, Pointer (Result)^, L);
  6595.   End;
  6596.  
  6597.  
  6598.  
  6599. {                                                                              }
  6600. { Dynamic array functions                                                      }
  6601. {                                                                              }
  6602. Function StringArrayLength (const S : Array of String) : Integer;
  6603. var I : Integer;
  6604.   Begin
  6605.     Result := 0;
  6606.     For I := 0 to Length (S) - 1 do
  6607.       Inc (Result, Length (S [I]));
  6608.   End;
  6609.  
  6610. Function LongestStringLength (const S : Array of String) : Integer;
  6611. var I, L : Integer;
  6612.   Begin
  6613.     Result := 0;
  6614.     For I := 0 to Length (S) - 1 do
  6615.       begin
  6616.         L := Length (S [I]);
  6617.         if L > Result then
  6618.           Result := L;
  6619.       end;
  6620.   End;
  6621.  
  6622. Function Append (var V : CharSetArray; const S : String; const CaseSensitive : Boolean) : Integer;
  6623. var I, L : Integer;
  6624.   Begin
  6625.     Result := Length (V);
  6626.     L := Length (S);
  6627.     if L > 0 then
  6628.       begin
  6629.         SetLength (V, Result + L);
  6630.         For I := 1 to L  do
  6631.           if CaseSensitive then
  6632.             V [Result + I - 1] := [S [I]] else
  6633.             V [Result + I - 1] := [LowCase (S [I]), UpCase (S [I])];
  6634.       end;
  6635.   End;
  6636.  
  6637. Function PosNext (const Find : String; const V : StringArray; const PrevPos : Integer;
  6638.          const IsSortedAscending : Boolean; const CaseSensitive : Boolean) : Integer;
  6639. var I, L, H : Integer;
  6640.   Begin
  6641.     if IsSortedAscending then // binary search
  6642.       begin
  6643.         if MaxI (PrevPos + 1, 0) = 0 then // find first
  6644.           begin
  6645.             L := 0;
  6646.             H := Length (V) - 1;
  6647.             Repeat
  6648.               I := (L + H) div 2;
  6649.               if IsEqual (V [I], Find, CaseSensitive) then
  6650.                 begin
  6651.                   While (I > 0) and IsEqual (V [I - 1], Find, CaseSensitive) do
  6652.                     Dec (I);
  6653.                   Result := I;
  6654.                   exit;
  6655.                 end else
  6656.               if (CaseSensitive and (V [I] > Find)) or
  6657.                  (not CaseSensitive and (LowerCase (V [I]) > LowerCase (Find))) then
  6658.                 H := I - 1 else
  6659.                 L := I + 1;
  6660.             Until L > H;
  6661.             Result := -1;
  6662.           end else // find next
  6663.           if PrevPos >= Length (V) - 1 then
  6664.             Result := -1 else
  6665.             if IsEqual (V [PrevPos + 1], Find, CaseSensitive) then
  6666.               Result := PrevPos + 1 else
  6667.               Result := -1;
  6668.       end else
  6669.       begin // linear search
  6670.         For I := MaxI (PrevPos + 1, 0) to Length (V) - 1 do
  6671.           if IsEqual (V [I], Find, CaseSensitive) then
  6672.             begin
  6673.               Result := I;
  6674.               exit;
  6675.             end;
  6676.         Result := -1;
  6677.       end;
  6678.   End;
  6679.  
  6680. Function SingleArrayToStringArray (const V : SingleArray) : StringArray;
  6681. var I, L : Integer;
  6682.   Begin
  6683.     L := Length (V);
  6684.     SetLength (Result, L);
  6685.     For I := 0 to L - 1 do
  6686.       Result [I] := FloatToStr (V [I]);
  6687.   End;
  6688.  
  6689. Function DoubleArrayToStringArray (const V : DoubleArray) : StringArray;
  6690. var I, L : Integer;
  6691.   Begin
  6692.     L := Length (V);
  6693.     SetLength (Result, L);
  6694.     For I := 0 to L - 1 do
  6695.       Result [I] := FloatToStr (V [I]);
  6696.   End;
  6697.  
  6698. Function ExtendedArrayToStringArray (const V : ExtendedArray) : StringArray;
  6699. var I, L : Integer;
  6700.   Begin
  6701.     L := Length (V);
  6702.     SetLength (Result, L);
  6703.     For I := 0 to L - 1 do
  6704.       Result [I] := FloatToStr (V [I]);
  6705.   End;
  6706.  
  6707. Function LongIntArrayToStringArray (const V : LongIntArray) : StringArray;
  6708. var I, L : Integer;
  6709.   Begin
  6710.     L := Length (V);
  6711.     SetLength (Result, L);
  6712.     For I := 0 to L - 1 do
  6713.       Result [I] := IntToStr (V [I]);
  6714.   End;
  6715.  
  6716. Function Int64ArrayToStringArray (const V : Int64Array) : StringArray;
  6717. var I, L : Integer;
  6718.   Begin
  6719.     L := Length (V);
  6720.     SetLength (Result, L);
  6721.     For I := 0 to L - 1 do
  6722.       Result [I] := IntToStr (V [I]);
  6723.   End;
  6724.  
  6725. Function StringArrayToLongIntArray (const V : StringArray) : LongIntArray;
  6726. var I, L : Integer;
  6727.   Begin
  6728.     L := Length (V);
  6729.     SetLength (Result, L);
  6730.     For I := 0 to L - 1 do
  6731.       Result [I] := StrToInt (V [I]);
  6732.   End;
  6733.  
  6734. Function StringArrayToInt64Array (const V : StringArray) : Int64Array;
  6735. var I, L : Integer;
  6736.   Begin
  6737.     L := Length (V);
  6738.     SetLength (Result, L);
  6739.     For I := 0 to L - 1 do
  6740.       Result [I] := StrToInt64 (V [I]);
  6741.   End;
  6742.  
  6743. Function StringArrayToSingleArray (const V : StringArray) : SingleArray;
  6744. var I, L : Integer;
  6745.   Begin
  6746.     L := Length (V);
  6747.     SetLength (Result, L);
  6748.     For I := 0 to L - 1 do
  6749.       Result [I] := StrToFloat (V [I]);
  6750.   End;
  6751.  
  6752. Function StringArrayToDoubleArray (const V : StringArray) : DoubleArray;
  6753. var I, L : Integer;
  6754.   Begin
  6755.     L := Length (V);
  6756.     SetLength (Result, L);
  6757.     For I := 0 to L - 1 do
  6758.       Result [I] := StrToFloat (V [I]);
  6759.   End;
  6760.  
  6761. Function StringArrayToExtendedArray (const V : StringArray) : ExtendedArray;
  6762. var I, L : Integer;
  6763.   Begin
  6764.     L := Length (V);
  6765.     SetLength (Result, L);
  6766.     For I := 0 to L - 1 do
  6767.       Result [I] := StrToFloat (V [I]);
  6768.   End;
  6769.  
  6770. Function ByteArrayToStr (const V : ByteArray; const ItemDelimiter : String) : String;
  6771. var I, L : Integer;
  6772.   Begin
  6773.     Result := '';
  6774.     L := Length (V);
  6775.     if L = 0 then
  6776.       exit;
  6777.     Result := IntToStr (V [0]);
  6778.     For I := 1 to L - 1 do
  6779.       Result := Result + ItemDelimiter + IntToStr (V [I]);
  6780.   End;
  6781.  
  6782. Function WordArrayToStr (const V : WordArray; const ItemDelimiter : String) : String;
  6783. var I, L : Integer;
  6784.   Begin
  6785.     Result := '';
  6786.     L := Length (V);
  6787.     if L = 0 then
  6788.       exit;
  6789.     Result := IntToStr (V [0]);
  6790.     For I := 1 to L - 1 do
  6791.       Result := Result + ItemDelimiter + IntToStr (V [I]);
  6792.   End;
  6793.  
  6794. Function LongWordArrayToStr (const V : LongWordArray; const ItemDelimiter : String) : String;
  6795. var I, L : Integer;
  6796.   Begin
  6797.     Result := '';
  6798.     L := Length (V);
  6799.     if L = 0 then
  6800.       exit;
  6801.     Result := IntToStr (V [0]);
  6802.     For I := 1 to L - 1 do
  6803.       Result := Result + ItemDelimiter + IntToStr (V [I]);
  6804.   End;
  6805.  
  6806. Function CardinalArrayToStr (const V : CardinalArray; const ItemDelimiter : String) : String;
  6807. var I, L : Integer;
  6808.   Begin
  6809.     Result := '';
  6810.     L := Length (V);
  6811.     if L = 0 then
  6812.       exit;
  6813.     Result := IntToStr (V [0]);
  6814.     For I := 1 to L - 1 do
  6815.       Result := Result + ItemDelimiter + IntToStr (V [I]);
  6816.   End;
  6817.  
  6818. Function ShortIntArrayToStr (const V : ShortIntArray; const ItemDelimiter : String) : String;
  6819. var I, L : Integer;
  6820.   Begin
  6821.     Result := '';
  6822.     L := Length (V);
  6823.     if L = 0 then
  6824.       exit;
  6825.     Result := IntToStr (V [0]);
  6826.     For I := 1 to L - 1 do
  6827.       Result := Result + ItemDelimiter + IntToStr (V [I]);
  6828.   End;
  6829.  
  6830. Function SmallIntArrayToStr (const V : SmallIntArray; const ItemDelimiter : String) : String;
  6831. var I, L : Integer;
  6832.   Begin
  6833.     Result := '';
  6834.     L := Length (V);
  6835.     if L = 0 then
  6836.       exit;
  6837.     Result := IntToStr (V [0]);
  6838.     For I := 1 to L - 1 do
  6839.       Result := Result + ItemDelimiter + IntToStr (V [I]);
  6840.   End;
  6841.  
  6842. Function LongIntArrayToStr (const V : LongIntArray; const ItemDelimiter : String) : String;
  6843. var I, L : Integer;
  6844.   Begin
  6845.     Result := '';
  6846.     L := Length (V);
  6847.     if L = 0 then
  6848.       exit;
  6849.     Result := IntToStr (V [0]);
  6850.     For I := 1 to L - 1 do
  6851.       Result := Result + ItemDelimiter + IntToStr (V [I]);
  6852.   End;
  6853.  
  6854. Function IntegerArrayToStr (const V : IntegerArray; const ItemDelimiter : String) : String;
  6855. var I, L : Integer;
  6856.   Begin
  6857.     Result := '';
  6858.     L := Length (V);
  6859.     if L = 0 then
  6860.       exit;
  6861.     Result := IntToStr (V [0]);
  6862.     For I := 1 to L - 1 do
  6863.       Result := Result + ItemDelimiter + IntToStr (V [I]);
  6864.   End;
  6865.  
  6866. Function Int64ArrayToStr (const V : Int64Array; const ItemDelimiter : String) : String;
  6867. var I, L : Integer;
  6868.   Begin
  6869.     Result := '';
  6870.     L := Length (V);
  6871.     if L = 0 then
  6872.       exit;
  6873.     Result := IntToStr (V [0]);
  6874.     For I := 1 to L - 1 do
  6875.       Result := Result + ItemDelimiter + IntToStr (V [I]);
  6876.   End;
  6877.  
  6878. Function SingleArrayToStr (const V : SingleArray; const ItemDelimiter : String) : String;
  6879. var I, L : Integer;
  6880.   Begin
  6881.     Result := '';
  6882.     L := Length (V);
  6883.     if L = 0 then
  6884.       exit;
  6885.     Result := FloatToStr (V [0]);
  6886.     For I := 1 to L - 1 do
  6887.       Result := Result + ItemDelimiter + FloatToStr (V [I]);
  6888.   End;
  6889.  
  6890. Function DoubleArrayToStr (const V : DoubleArray; const ItemDelimiter : String) : String;
  6891. var I, L : Integer;
  6892.   Begin
  6893.     Result := '';
  6894.     L := Length (V);
  6895.     if L = 0 then
  6896.       exit;
  6897.     Result := FloatToStr (V [0]);
  6898.     For I := 1 to L - 1 do
  6899.       Result := Result + ItemDelimiter + FloatToStr (V [I]);
  6900.   End;
  6901.  
  6902. Function ExtendedArrayToStr (const V : ExtendedArray; const ItemDelimiter : String) : String;
  6903. var I, L : Integer;
  6904.   Begin
  6905.     Result := '';
  6906.     L := Length (V);
  6907.     if L = 0 then
  6908.       exit;
  6909.     Result := FloatToStr (V [0]);
  6910.     For I := 1 to L - 1 do
  6911.       Result := Result + ItemDelimiter + FloatToStr (V [I]);
  6912.   End;
  6913.  
  6914. Function StringArrayToStr (const V : StringArray; const ItemDelimiter : String; const QuoteItems : Boolean; const Quote : Char) : String;
  6915. var I, L : Integer;
  6916.   Begin
  6917.     Result := '';
  6918.     L := Length (V);
  6919.     if L = 0 then
  6920.       exit else
  6921.     Result := V [0];
  6922.     if QuoteItems then
  6923.       Result := QuoteText (Result, Quote);
  6924.     For I := 1 to L - 1 do
  6925.       if not QuoteItems then
  6926.         Result := Result + ItemDelimiter + V [I] else
  6927.         Result := Result + ItemDelimiter + QuoteText (V [I], Quote);
  6928.   End;
  6929.  
  6930. {                                                                              }
  6931. { String to Dynamic array                                                      }
  6932. {                                                                              }
  6933. Function StrToByteArray (const S : String; const Delimiter : Char) : ByteArray;
  6934. var F, G, L, C : Integer;
  6935.   Begin
  6936.     L := 0;
  6937.     F := 1;
  6938.     C := Length (S);
  6939.     While F <= C do
  6940.       begin
  6941.         G := 0;
  6942.         While (F + G <= C) and (S [F + G] <> Delimiter) do
  6943.           Inc (G);
  6944.         Inc (L);
  6945.         SetLength (Result, L);
  6946.         if G = 0 then
  6947.           Result [L - 1] := 0 else
  6948.           Result [L - 1] := StrToInt (Copy (S, F, G));
  6949.         Inc (F, G + 1);
  6950.       end;
  6951.   End;
  6952.  
  6953. Function StrToWordArray (const S : String; const Delimiter : Char) : WordArray;
  6954. var F, G, L, C : Integer;
  6955.   Begin
  6956.     L := 0;
  6957.     F := 1;
  6958.     C := Length (S);
  6959.     While F <= C do
  6960.       begin
  6961.         G := 0;
  6962.         While (F + G <= C) and (S [F + G] <> Delimiter) do
  6963.           Inc (G);
  6964.         Inc (L);
  6965.         SetLength (Result, L);
  6966.         if G = 0 then
  6967.           Result [L - 1] := 0 else
  6968.           Result [L - 1] := StrToInt (Copy (S, F, G));
  6969.         Inc (F, G + 1);
  6970.       end;
  6971.   End;
  6972.  
  6973. Function StrToLongWordArray (const S : String; const Delimiter : Char) : LongWordArray;
  6974. var F, G, L, C : Integer;
  6975.   Begin
  6976.     L := 0;
  6977.     F := 1;
  6978.     C := Length (S);
  6979.     While F <= C do
  6980.       begin
  6981.         G := 0;
  6982.         While (F + G <= C) and (S [F + G] <> Delimiter) do
  6983.           Inc (G);
  6984.         Inc (L);
  6985.         SetLength (Result, L);
  6986.         if G = 0 then
  6987.           Result [L - 1] := 0 else
  6988.           Result [L - 1] := StrToInt (Copy (S, F, G));
  6989.         Inc (F, G + 1);
  6990.       end;
  6991.   End;
  6992.  
  6993. Function StrToCardinalArray (const S : String; const Delimiter : Char) : CardinalArray;
  6994. var F, G, L, C : Integer;
  6995.   Begin
  6996.     L := 0;
  6997.     F := 1;
  6998.     C := Length (S);
  6999.     While F <= C do
  7000.       begin
  7001.         G := 0;
  7002.         While (F + G <= C) and (S [F + G] <> Delimiter) do
  7003.           Inc (G);
  7004.         Inc (L);
  7005.         SetLength (Result, L);
  7006.         if G = 0 then
  7007.           Result [L - 1] := 0 else
  7008.           Result [L - 1] := StrToInt (Copy (S, F, G));
  7009.         Inc (F, G + 1);
  7010.       end;
  7011.   End;
  7012.  
  7013. Function StrToShortIntArray (const S : String; const Delimiter : Char) : ShortIntArray;
  7014. var F, G, L, C : Integer;
  7015.   Begin
  7016.     L := 0;
  7017.     F := 1;
  7018.     C := Length (S);
  7019.     While F <= C do
  7020.       begin
  7021.         G := 0;
  7022.         While (F + G <= C) and (S [F + G] <> Delimiter) do
  7023.           Inc (G);
  7024.         Inc (L);
  7025.         SetLength (Result, L);
  7026.         if G = 0 then
  7027.           Result [L - 1] := 0 else
  7028.           Result [L - 1] := StrToInt (Copy (S, F, G));
  7029.         Inc (F, G + 1);
  7030.       end;
  7031.   End;
  7032.  
  7033. Function StrToSmallIntArray (const S : String; const Delimiter : Char) : SmallIntArray;
  7034. var F, G, L, C : Integer;
  7035.   Begin
  7036.     L := 0;
  7037.     F := 1;
  7038.     C := Length (S);
  7039.     While F <= C do
  7040.       begin
  7041.         G := 0;
  7042.         While (F + G <= C) and (S [F + G] <> Delimiter) do
  7043.           Inc (G);
  7044.         Inc (L);
  7045.         SetLength (Result, L);
  7046.         if G = 0 then
  7047.           Result [L - 1] := 0 else
  7048.           Result [L - 1] := StrToInt (Copy (S, F, G));
  7049.         Inc (F, G + 1);
  7050.       end;
  7051.   End;
  7052.  
  7053. Function StrToLongIntArray (const S : String; const Delimiter : Char) : LongIntArray;
  7054. var F, G, L, C : Integer;
  7055.   Begin
  7056.     L := 0;
  7057.     F := 1;
  7058.     C := Length (S);
  7059.     While F <= C do
  7060.       begin
  7061.         G := 0;
  7062.         While (F + G <= C) and (S [F + G] <> Delimiter) do
  7063.           Inc (G);
  7064.         Inc (L);
  7065.         SetLength (Result, L);
  7066.         if G = 0 then
  7067.           Result [L - 1] := 0 else
  7068.           Result [L - 1] := StrToInt (Copy (S, F, G));
  7069.         Inc (F, G + 1);
  7070.       end;
  7071.   End;
  7072.  
  7073. Function StrToIntegerArray (const S : String; const Delimiter : Char) : IntegerArray;
  7074. var F, G, L, C : Integer;
  7075.   Begin
  7076.     L := 0;
  7077.     F := 1;
  7078.     C := Length (S);
  7079.     While F <= C do
  7080.       begin
  7081.         G := 0;
  7082.         While (F + G <= C) and (S [F + G] <> Delimiter) do
  7083.           Inc (G);
  7084.         Inc (L);
  7085.         SetLength (Result, L);
  7086.         if G = 0 then
  7087.           Result [L - 1] := 0 else
  7088.           Result [L - 1] := StrToInt (Copy (S, F, G));
  7089.         Inc (F, G + 1);
  7090.       end;
  7091.   End;
  7092.  
  7093. Function StrToInt64Array (const S : String; const Delimiter : Char) : Int64Array;
  7094. var F, G, L, C : Integer;
  7095.   Begin
  7096.     L := 0;
  7097.     F := 1;
  7098.     C := Length (S);
  7099.     While F <= C do
  7100.       begin
  7101.         G := 0;
  7102.         While (F + G <= C) and (S [F + G] <> Delimiter) do
  7103.           Inc (G);
  7104.         Inc (L);
  7105.         SetLength (Result, L);
  7106.         if G = 0 then
  7107.           Result [L - 1] := 0 else
  7108.           Result [L - 1] := StrToInt64 (Copy (S, F, G));
  7109.         Inc (F, G + 1);
  7110.       end;
  7111.   End;
  7112.  
  7113. Function StrToSingleArray (const S : String; const Delimiter : Char) : SingleArray;
  7114. var F, G, L, C : Integer;
  7115.   Begin
  7116.     L := 0;
  7117.     F := 1;
  7118.     C := Length (S);
  7119.     While F <= C do
  7120.       begin
  7121.         G := 0;
  7122.         While (F + G <= C) and (S [F + G] <> Delimiter) do
  7123.           Inc (G);
  7124.         Inc (L);
  7125.         SetLength (Result, L);
  7126.         if G = 0 then
  7127.           Result [L - 1] := 0.0 else
  7128.           Result [L - 1] := StrToFloat (Copy (S, F, G));
  7129.         Inc (F, G + 1);
  7130.       end;
  7131.   End;
  7132.  
  7133. Function StrToDoubleArray (const S : String; const Delimiter : Char) : DoubleArray;
  7134. var F, G, L, C : Integer;
  7135.   Begin
  7136.     L := 0;
  7137.     F := 1;
  7138.     C := Length (S);
  7139.     While F <= C do
  7140.       begin
  7141.         G := 0;
  7142.         While (F + G <= C) and (S [F + G] <> Delimiter) do
  7143.           Inc (G);
  7144.         Inc (L);
  7145.         SetLength (Result, L);
  7146.         if G = 0 then
  7147.           Result [L - 1] := 0.0 else
  7148.           Result [L - 1] := StrToFloat (Copy (S, F, G));
  7149.         Inc (F, G + 1);
  7150.       end;
  7151.   End;
  7152.  
  7153. Function StrToExtendedArray (const S : String; const Delimiter : Char) : ExtendedArray;
  7154. var F, G, L, C : Integer;
  7155.   Begin
  7156.     L := 0;
  7157.     F := 1;
  7158.     C := Length (S);
  7159.     While F <= C do
  7160.       begin
  7161.         G := 0;
  7162.         While (F + G <= C) and (S [F + G] <> Delimiter) do
  7163.           Inc (G);
  7164.         Inc (L);
  7165.         SetLength (Result, L);
  7166.         if G = 0 then
  7167.           Result [L - 1] := 0.0 else
  7168.           Result [L - 1] := StrToFloat (Copy (S, F, G));
  7169.         Inc (F, G + 1);
  7170.       end;
  7171.   End;
  7172.  
  7173. Function StrToStringArray (const S : String; const Delimiter : Char) : StringArray;
  7174. var F, G, L, C : Integer;
  7175.   Begin
  7176.     L := 0;
  7177.     F := 1;
  7178.     C := Length (S);
  7179.     While F <= C do
  7180.       begin
  7181.         G := 0;
  7182.         While (F + G <= C) and (S [F + G] <> Delimiter) do
  7183.           Inc (G);
  7184.         Inc (L);
  7185.         SetLength (Result, L);
  7186.         if G = 0 then
  7187.           Result [L - 1] := '' else
  7188.           Result [L - 1] := UnquoteText (Copy (S, F, G));
  7189.         Inc (F, G + 1);
  7190.       end;
  7191.   End;
  7192.  
  7193.  
  7194.  
  7195. {                                                                              }
  7196. { Self testing code                                                            }
  7197. {                                                                              }
  7198. Procedure Test_Copy;
  7199.   Begin
  7200.     { CopyRange                                                            }
  7201.     Assert (CopyRange ('', 1, 2) =  '', 'CopyRange');
  7202.     Assert (CopyRange ('', -1, -2) = '', 'CopyRange');
  7203.     Assert (CopyRange ('1234567890', 5, 7) = '567', 'CopyRange');
  7204.     Assert (CopyRange ('1234567890', 1, 1) = '1', 'CopyRange');
  7205.     Assert (CopyRange ('1234567890', 0, 11) = '1234567890', 'CopyRange');
  7206.     Assert (CopyRange ('1234567890', 7, 4) = '', 'CopyRange');
  7207.     Assert (CopyRange ('1234567890', 1, 0) = '', 'CopyRange');
  7208.     Assert (CopyRange ('1234567890', -2, 3) = '123', 'CopyRange');
  7209.     Assert (CopyRange ('1234567890', 2, -1) = '', 'CopyRange');
  7210.     Assert (CopyRange ('1234567890', -4, -2) = '', 'CopyRange');
  7211.  
  7212.     { CopyFrom                                                             }
  7213.     Assert (CopyFrom ('a', 0) = 'a', 'CopyFrom');
  7214.     Assert (CopyFrom ('a', -1) = 'a', 'CopyFrom');
  7215.     Assert (CopyFrom ('', 1) = '', 'CopyFrom');
  7216.     Assert (CopyFrom ('', -2) = '', 'CopyFrom');
  7217.     Assert (CopyFrom ('1234567890', 8) = '890', 'CopyFrom');
  7218.     Assert (CopyFrom ('1234567890', 11) = '', 'CopyFrom');
  7219.     Assert (CopyFrom ('1234567890', 0) = '1234567890', 'CopyFrom');
  7220.     Assert (CopyFrom ('1234567890', -2) = '1234567890', 'CopyFrom');
  7221.  
  7222.     { CopyLeft                                                             }
  7223.     Assert (CopyLeft ('a', 0) = '', 'CopyLeft');
  7224.     Assert (CopyLeft ('a', -1) = '', 'CopyLeft');
  7225.     Assert (CopyLeft ('', 1) = '', 'CopyLeft');
  7226.     Assert (CopyLeft ('b', 1) = 'b', 'CopyLeft');
  7227.     Assert (CopyLeft ('', -1) = '', 'CopyLeft');
  7228.     Assert (CopyLeft ('1234567890', 3) = '123', 'CopyLeft');
  7229.     Assert (CopyLeft ('1234567890', 11) = '1234567890', 'CopyLeft');
  7230.     Assert (CopyLeft ('1234567890', 0) = '', 'CopyLeft');
  7231.     Assert (CopyLeft ('1234567890', -2) = '', 'CopyLeft');
  7232.  
  7233.     { CopyRight                                                            }
  7234.     Assert (CopyRight ('a', 0) = '', 'CopyRight');
  7235.     Assert (CopyRight ('a', -1) = '', 'CopyRight');
  7236.     Assert (CopyRight ('', 1) = '', 'CopyRight');
  7237.     Assert (CopyRight ('', -2) = '', 'CopyRight');
  7238.     Assert (CopyRight ('1234567890', 3) = '890', 'CopyRight');
  7239.     Assert (CopyRight ('1234567890', 11) = '1234567890', 'CopyRight');
  7240.     Assert (CopyRight ('1234567890', 0) = '', 'CopyRight');
  7241.     Assert (CopyRight ('1234567890', -2) = '', 'CopyRight');
  7242.   End;
  7243.  
  7244. Procedure Test_CopyEx;
  7245.   Begin
  7246.     { CopyEx                                                               }
  7247.     Assert (CopyEx ('', 1, 1) = '');
  7248.     Assert (CopyEx ('', -2, -1) = '');
  7249.     Assert (CopyEx ('12345', -2, 2) = '45');
  7250.     Assert (CopyEx ('12345', -1, 2) = '5');
  7251.     Assert (CopyEx ('12345', -7, 2) = '12');
  7252.     Assert (CopyEx ('12345', -5, 2) = '12');
  7253.     Assert (CopyEx ('12345', 2, -2) = '');
  7254.     Assert (CopyEx ('12345', -4, 0) = '');
  7255.     Assert (CopyEx ('12345', -4, 7) = '2345');
  7256.     Assert (CopyEx ('12345', 2, 2) = '23');
  7257.     Assert (CopyEx ('12345', -7, -6) = '');
  7258.     Assert (CopyEx ('12345', 0, 2) = '12');
  7259.     Assert (CopyEx ('12345', 0, 7) = '12345');
  7260.  
  7261.     { CopyRangeEx                                                          }
  7262.     Assert (CopyRangeEx ('', -2, -1) = '');
  7263.     Assert (CopyRangeEx ('', 0, 0) = '');
  7264.     Assert (CopyRangeEx ('12345', -2, -1) = '45');
  7265.     Assert (CopyRangeEx ('12345', -2, -1) = '45');
  7266.     Assert (CopyRangeEx ('12345', -2, 5) = '45');
  7267.     Assert (CopyRangeEx ('12345', 2, -2) = '234');
  7268.     Assert (CopyRangeEx ('12345', 0, -2) = '1234');
  7269.     Assert (CopyRangeEx ('12345', 1, -7) = '');
  7270.     Assert (CopyRangeEx ('12345', 7, -1) = '');
  7271.     Assert (CopyRangeEx ('12345', -10, 2) = '12');
  7272.     Assert (CopyRangeEx ('12345', -10, -7) = '');
  7273.     Assert (CopyRangeEx ('12345', 2, -6) = '');
  7274.     Assert (CopyRangeEx ('12345', 0, -2) = '1234');
  7275.     Assert (CopyRangeEx ('12345', 2, 0) = '');
  7276.     Assert (CopyRangeEx ('', -1, 2) = '');
  7277.  
  7278.     { CopyFromEx                                                           }
  7279.     Assert (CopyFromEx ('', 0) = '');
  7280.     Assert (CopyFromEx ('', -1) = '');
  7281.     Assert (CopyFromEx ('12345', 0) = '12345');
  7282.     Assert (CopyFromEx ('12345', 1) = '12345');
  7283.     Assert (CopyFromEx ('12345', -5) = '12345');
  7284.     Assert (CopyFromEx ('12345', -6) = '12345');
  7285.     Assert (CopyFromEx ('12345', 2) = '2345');
  7286.     Assert (CopyFromEx ('12345', -4) =  '2345');
  7287.     Assert (CopyFromEx ('12345', 6) = '');
  7288.   End;
  7289.  
  7290. Procedure Test_Match;
  7291.   Begin
  7292.     { Match                                                                }
  7293.     Assert (not Match ([], ''));
  7294.     Assert (not Match ([], 'a'));
  7295.     Assert (not Match (['a'], ''));
  7296.     Assert (not Match ('', ''));
  7297.     Assert (not Match ('a', ''));
  7298.     Assert (not Match ('', 'a'));
  7299.     Assert (not Match ('A', 'a'));
  7300.     Assert (MatchChars ('A', ['a', 'A']) = 1);
  7301.     Assert (Match ('A', 'A'));
  7302.     Assert (Match ('A', 'a', False));
  7303.     Assert (Match ('a', 'A', False));
  7304.     Assert (Match ('A', 'A', False));
  7305.     Assert (Match (['a'..'z'], 'abcd', 2, 2));
  7306.     Assert (not Match (['a'..'z'], 'ab', 2, 2));
  7307.     Assert (not Match (['a'..'z'], 'abcd', 0, 2));
  7308.     Assert (Match (['a'..'z'], 'abcd', 1, 1));
  7309.     Assert (Match (['a'..'z'], 'abcd', 1, 0));
  7310.     Assert (Match (['y'..'z'], 'abcd', 1, 0));
  7311.     Assert (Match (['y'..'z'], 'abcd', 1, -1));
  7312.     Assert (not Match ('xx', 'abcdef', 1));
  7313.     Assert (Match ('x', 'xbcdef', 1));
  7314.     Assert (Match ('xxxxx', 'abcdxxxxx', 5));
  7315.     Assert (Match ('abcdef', 'abcdef', 1));
  7316.     Assert (not Match ('xx', 'abcdef', 1, False));
  7317.     Assert (Match ('xBCd', 'xbCDef', 1, False));
  7318.     Assert (Match ('Xxx-xX', 'abcdxxX-xx', 5, False));
  7319.     Assert (Match ('abcd', 'abcde', 1, True));
  7320.     Assert (Match ('abc', 'abcde', 1, True));
  7321.     Assert (Match ('ab', 'abcde', 1, True));
  7322.     Assert (Match ('a', 'abcde', 1, True));
  7323.     Assert (Match (' abC-Def{', ' AbC-def{', 1, False));
  7324.     Assert (Match (['a'..'z'], 'a', False));
  7325.     Assert (Match (['a'..'z'], 'A', False));
  7326.     Assert (not Match (['a'..'z'], '-', False));
  7327.     Assert (not Match (['a'..'z'], 'A', True));
  7328.     Assert (not Match ([], 'A'));
  7329.     Assert (MatchLeft ('aBc1', 'ABC1D', False), 'MatchLeft');
  7330.     Assert (MatchLeft ('aBc1', 'aBc1D', True), 'MatchLeft');
  7331.     Assert (not MatchLeft ('ABc1', 'AB1D', False), 'MatchLeft');
  7332.     Assert (not MatchLeft ('aBc1', 'aBC1D', True), 'MatchLeft');
  7333.     Assert (MatchCount ('a', 'baaab', 2, 5) = 3, 'MatchCount');
  7334.     Assert (MatchCount ('a', 'baaab', 2, 2) = 2, 'MatchCount');
  7335.     Assert (MatchCount ('a', 'baaab', 2, 0) = 0, 'MatchCount');
  7336.     Assert (MatchCount ('a', 'baaab', 1, 5) = 0, 'MatchCount');
  7337.     Assert (MatchCount ('a', 'aaab', -1, 5) = 3, 'MatchCount');
  7338.     Assert (MatchCount ('a', 'aaab', -1, 1) = 1, 'MatchCount');
  7339.  
  7340.     { MatchFileMask                                                        }
  7341.     Assert (MatchFileMask ('*', 'A'), 'MatchFileMask.1');
  7342.     Assert (MatchFileMask ('?', 'A'), 'MatchFileMask.2');
  7343.     Assert (MatchFileMask ('', 'A'), 'MatchFileMask.3');
  7344.     Assert (MatchFileMask ('', ''), 'MatchFileMask.4');
  7345.     Assert (not MatchFileMask ('X', ''), 'MatchFileMask.5');
  7346.     Assert (MatchFileMask ('A?', 'A'), 'MatchFileMask.6');
  7347.     Assert (MatchFileMask ('A?', 'AB'), 'MatchFileMask.7');
  7348.     Assert (MatchFileMask ('A*B*C', 'ACBDC'), 'MatchFileMask.8');
  7349.     Assert (MatchFileMask ('A*B*?', 'ACBDC'), 'MatchFileMask.9');
  7350.   End;
  7351.  
  7352. Procedure Test_Pos;
  7353. var ChS : CharSet;
  7354.   Begin
  7355.     { Pos                                                                  }
  7356.     ChS := [];
  7357.     Assert (Pos ('', 'a') = 0);
  7358.     Assert (Pos (ChS, 'a') = 0);
  7359.     Assert (PosSeq (AsCharSetArray (ChS), 'a') = 0);
  7360.     ChS := ['a'];
  7361.     Assert (Pos ('a', 'a') = 1);
  7362.     Assert (Pos (ChS, 'a') = 1);
  7363.     Assert (Pos ('a', '') = 0);
  7364.     Assert (Pos (ChS, '') = 0);
  7365.     Assert (Pos ('a', 'aa') = 1);
  7366.     Assert (Pos (ChS, 'aa') = 1);
  7367.     Assert (Pos ('a', 'ba') = 2);
  7368.     Assert (Pos (ChS, 'ba') = 2);
  7369.     Assert (Pos ('a', 'zx') = 0);
  7370.     Assert (Pos (ChS, 'zx') = 0);
  7371.     Assert (PosSeq (AsCharSetArray (ChS), 'a') = 1);
  7372.     Assert (PosSeq (AsCharSetArray (ChS), 'a') = 1);
  7373.     Assert (PosSeq (AsCharSetArray (ChS), 'ba') = 2);
  7374.     Assert (PosSeq (AsCharSetArray ([ChS, ChS]), 'a') = 0);
  7375.     Assert (PosSeq (AsCharSetArray ([ChS, ChS]), 'aa') = 1);
  7376.     Assert (PosSeq (AsCharSetArray ([ChS, ['a'..'z']]), 'ak') = 1);
  7377.     Assert (Pos ('ab', 'a') = 0);
  7378.     Assert (Pos ('ab', 'ab') = 1);
  7379.     Assert (Pos ('ab', 'zxab') = 3);
  7380.     Assert (Pos ('ab', '') = 0);
  7381.     Assert (Pos ('ab', 'axdba') = 0);
  7382.  
  7383.     ChS := ['a'..'z'];
  7384.     Assert (Pos ('a', 'abac', [foReverse]) = 3);
  7385.     Assert (Pos (ChS, 'abac', [foReverse]) = 4);
  7386.     Assert (Pos ('ab', 'abacabac', [foReverse]) = 5);
  7387.     Assert (PosSeq (AsCharSetArray ([ChS, ChS]), 'aa-b-cc', [foReverse]) = 6);
  7388.  
  7389.     Assert (Pos ('a', 'abac', [foNonMatch]) = 2);
  7390.     Assert (Pos (ChS, 'abac1a', [foNonMatch]) = 5);
  7391.     Assert (Pos ('ab', 'abacabac', [foNonMatch]) = 3);
  7392.     Assert (Pos ('aa', 'aaacabac', [foNonMatch]) = 3);
  7393.     Assert (PosSeq (AsCharSetArray ([ChS, ChS]), 'aa-b-cc', [foNonMatch]) = 3);
  7394.     Assert (PosSeq (AsCharSetArray ([ChS, ChS]), 'aabc-cc', [foNonMatch]) = 5);
  7395.  
  7396.     Assert (Pos ('a', 'AbAc', [foCaseInsensitive]) = 1);
  7397.     Assert (Pos (ChS, 'AbAc', [foCaseInsensitive]) = 1);
  7398.     Assert (Pos ('ba', 'ABAcabac', [foCaseInsensitive]) = 2);
  7399.     Assert (PosSeq (AsCharSetArray ([ChS, ChS]), '-AC-b-cc', [foCaseInsensitive]) = 2);
  7400.  
  7401.     Assert (Pos ('aa', 'aab', [foOverlapping, foNonMatch]) = 2);
  7402.     Assert (Pos ('aa', 'aab', [foNonMatch]) = 0);
  7403.     Assert (PosSeq ([['a'], ['a']], 'aab', [foOverlapping, foNonMatch]) = 2);
  7404.     Assert (PosSeq ([['a'], ['a']], 'aab', [foNonMatch]) = 0);
  7405.  
  7406.     ChS := ['a'];
  7407.     Assert (Pos ('a', 'abac', [], 2) = 3);
  7408.     Assert (Pos (ChS, 'abac', [], 2) = 3);
  7409.     Assert (Pos ('ab', 'abacabac', [], 2) = 5);
  7410.     Assert (PosSeq ([ChS, ChS], 'aa-b-aa', [], 2) = 6);
  7411.     Assert (Pos ('a', 'accca', [], 2, 4) = 0);
  7412.     Assert (Pos (ChS, 'accca', [], 2, 4) = 0);
  7413.     Assert (Pos ('ab', 'abbbab', [], 2, 5) = 0);
  7414.     Assert (PosSeq ([ChS, ChS], 'aabbbaa', [], 2, 5) = 0);
  7415.  
  7416.     { PosBMH                                                               }
  7417.     Assert (PosBMH ('', 'ABCD012012345') = 0, 'PosBMH');
  7418.     Assert (PosBMH ('123', '') = 0, 'PosBMH');
  7419.     Assert (PosBMH ('', '') = 0, 'PosBMH');
  7420.     Assert (PosBMH ('123', 'ABCD012012345') = 9, 'PosBMH');
  7421.     Assert (PosBMH ('12', 'ABCD012012345') = 6, 'PosBMH');
  7422.     Assert (PosBMH ('123', 'ABCD012012345', 0, 0) = 9, 'PosBMH');
  7423.     Assert (PosBMH ('123', 'ABCD012012345', 9) = 9, 'PosBMH');
  7424.     Assert (PosBMH ('123', 'ABCD012012345', 10) = 0, 'PosBMH');
  7425.     Assert (PosBMH ('123', 'ABCD012012345', 9, 9) = 9, 'PosBMH');
  7426.     Assert (PosBMH ('123', 'ABCD012012345', 15, 20) = 0, 'PosBMH');
  7427.   End;
  7428.  
  7429. Procedure Test_CopyDelim;
  7430.   Begin
  7431.     { CopyBefore                                                           }
  7432.     Assert (CopyBefore ('1234543210', '4', True) = '123');
  7433.     Assert (CopyBefore ('1234543210', '4', False) = '123');
  7434.     Assert (CopyBefore ('1234543210', '6', True) = '1234543210');
  7435.     Assert (CopyBefore ('1234543210', '6', False) = '');
  7436.     Assert (CopyBefore ('1234543210', ['2', '4'], True) = '1');
  7437.     Assert (CopyBefore ('1234543210', ['2', '4'], False) = '1');
  7438.     Assert (CopyBefore ('1234543210', ['6', 'a'], True) = '1234543210');
  7439.     Assert (CopyBefore ('1234543210', ['6', 'a'], False) = '');
  7440.  
  7441.     { CopyAfter                                                            }
  7442.     Assert (CopyAfter ('1234543210', '4', True) = '543210');
  7443.     Assert (CopyAfter ('1234543210', '4', False) = '543210');
  7444.     Assert (CopyAfter ('1234543210', '6', True) = '1234543210');
  7445.     Assert (CopyAfter ('1234543210', '6', False) = '');
  7446.     Assert (CopyAfter ('1234543210', ['4', '5'], True) = '543210');
  7447.     Assert (CopyAfter ('1234543210', ['4', '5'], False) = '543210');
  7448.     Assert (CopyAfter ('1234543210', ['6', 'a'], True) = '1234543210');
  7449.     Assert (CopyAfter ('1234543210', ['6', 'a'], False) = '');
  7450.  
  7451.     { CopyFrom                                                             }
  7452.     Assert (CopyFrom ('1234543210', '4') = '4543210');
  7453.     Assert (CopyFrom ('1234543210', '6') = '');
  7454.     Assert (CopyFrom ('1234543210', '9', True) = '1234543210');
  7455.     Assert (CopyFrom ('1234543210', '4', True, [], 5) = '43210');
  7456.     Assert (CopyFrom ('1234543210', '6', True, [], 5) = '543210');
  7457.     Assert (CopyFrom ('1234543210', ['4', '5']) = '4543210');
  7458.  
  7459.     { CopyTo                                                               }
  7460.     Assert (CopyTo ('1234543210', '4', True) = '1234');
  7461.     Assert (CopyTo ('1234543210', '4', False) = '1234');
  7462.     Assert (CopyTo ('1234543210', '6', True) = '1234543210');
  7463.     Assert (CopyTo ('1234543210', '6', False) = '');
  7464.  
  7465.     { CopyBetween                                                          }
  7466.     Assert (CopyBetween ('1234543210', '3', '3', False, False) = '454');
  7467.     Assert (CopyBetween ('1234543210', '3', '4', False, False) = '');
  7468.     Assert (CopyBetween ('1234543210', '4', '3', False, False) = '54');
  7469.     Assert (CopyBetween ('1234543210', '4', '6', False, False) = '');
  7470.     Assert (CopyBetween ('1234543210', '4', '6', False, True) = '543210');
  7471.     Assert (CopyBetween ('1234543210', '3', ['2', '3'], False,  False) = '454');
  7472.     Assert (CopyBetween ('1234543210', '3', ['4', '5'], False, False) = '');
  7473.     Assert (CopyBetween ('1234543210', '4', ['2', '3'], False, False) = '54');
  7474.     Assert (CopyBetween ('1234543210', '4', ['6', '7'], False, False) = '');
  7475.     Assert (CopyBetween ('1234543210', '4', ['6'], False, True) = '543210');
  7476.   End;
  7477.  
  7478. Procedure Test_Replace;
  7479.   Begin
  7480.     { Replace                                                              }
  7481.     Assert (Replace ('a', 'b', 'bababa') = 'bbbbbb');
  7482.     Assert (Replace ('a', '', 'bababa') = 'bbb');
  7483.     Assert (Replace ('a', '', 'aaa') = '');
  7484.     Assert (Replace ('aba', 'x', 'bababa') = 'bxba');
  7485.     Assert (Replace ('b', 'bb', 'bababa') = 'bbabbabba');
  7486.     Assert (Replace ('c', 'aa', 'bababa') = 'bababa');
  7487.     Assert (Replace ('ba', '', 'bababa') = '');
  7488.     Assert (Replace ('BA', '', 'bababa', [foCaseInsensitive]) = '');
  7489.     Assert (Replace ('BA', 'X', 'bababa', [foCaseInsensitive]) = 'XXX');
  7490.     Assert (Replace ('BA', 'X', 'bababa', [foCaseInsensitive], 2) = 'aXX');
  7491.     Assert (Replace ('aa', '12', 'aaaaa') = '1212a');
  7492.     Assert (Replace ('aa', 'a', 'aaaaa') = 'aaa');
  7493.     Assert (Replace (['b'], 'z', 'bababa') = 'zazaza');
  7494.     Assert (Replace (['b', 'a'], 'z', 'bababa') = 'zzzzzz');
  7495.     Assert (QuoteText ('Abe''s', '''') = '''Abe''''s''', 'QuoteText');
  7496.     Assert (RemoveAll (['a', 'z'], 'bazabazza') = 'bb', 'Remove');
  7497.     Assert (RemoveDup ('a', 'azaazzel') = 'azazzel', 'RemoveDup');
  7498.     Assert (Replace ('a', 'b', 'bababaa', [foReverse], 1, -2, 2) = 'babbbba');
  7499.   End;
  7500.  
  7501. Procedure Test_Count;
  7502. const C : CharSet = ['a'..'z'];
  7503.   Begin
  7504.     { Count                                                                }
  7505.     Assert (Count ('xyz', 'abcxyzdexxyxyz') = 2);
  7506.     Assert (Count ('xx', 'axxbxxxx') = 3);
  7507.     Assert (Count ('xx', 'axxbxxx') = 2);
  7508.     Assert (Count ('x', 'abcxyzdexxyxyz') = 4);
  7509.     Assert (Count ('q', 'abcxyzdexxyxyz') = 0);
  7510.     Assert (Count (C, 'abcxyzdexxyxyz') = 14);
  7511.   End;
  7512.  
  7513. Procedure Test_PosEx;
  7514.   Begin
  7515.     { PosEx                                                                }
  7516.     Assert (PosEx ('A', 'XAXAAXAAA', [], 1, -1, 1) = 2);
  7517.     Assert (PosEx ('A', 'XAXAAXAAA', [], 1, -1, 3) = 5);
  7518.     Assert (PosEx ('A', 'XAXAAXAAA', [], 1, -1, 6) = 9);
  7519.     Assert (PosEx ('A', 'XAXAAXAAA', [], 1, -1, 7) = 0);
  7520.   End;
  7521.  
  7522. Procedure Test_Case;
  7523. var S : String;
  7524.     Ch : Char;
  7525.   Begin
  7526.     { Case functions                                                       }
  7527.     For Ch := #0 to #255 do
  7528.       begin
  7529.         Assert (UpCase (Ch) = UpperCase (Ch), 'UpCase = UpperCase');
  7530.         Assert (LowCase (Ch) = LowerCase (Ch), 'UpCase = UpperCase');
  7531.       end;
  7532.     For Ch := 'A' to 'Z' do
  7533.       begin
  7534.         Assert (LowCase (Ch) <> Ch, 'LowCase');
  7535.         Assert (UpCase (Ch) = Ch, 'UpCase');
  7536.       end;
  7537.     For Ch := 'a' to 'z' do
  7538.       begin
  7539.         Assert (UpCase (Ch) <> Ch, 'LowCase');
  7540.         Assert (LowCase (Ch) = Ch, 'UpCase');
  7541.       end;
  7542.     Assert (['a'..'c', 'A'..'C'] = CaseInsensitiveCharSet (['A', 'b', 'C']), 'InsensitiveCharSet');
  7543.     Assert (FirstUp ('abra') = 'Abra', 'FirstUp');
  7544.     Assert (FirstUp ('') = '', 'FirstUp');
  7545.     Assert (LowCase ('A') = 'a', 'LowCase');
  7546.     Assert (UpCase ('a') = 'A', 'UpCase');
  7547.     Assert (LowCase ('-') = '-', 'LowCase');
  7548.     Assert (UpCase ('}') = '}', 'UpCase');
  7549.     S := 'aBcDEfg-123';
  7550.     ConvertUpper (S);
  7551.     Assert (S = 'ABCDEFG-123', 'ConvertUpper');
  7552.     S := 'aBcDEfg-123';
  7553.     ConvertLower (S);
  7554.     Assert (S = 'abcdefg-123', 'ConvertLower');
  7555.     S := '';
  7556.     ConvertLower (S);
  7557.     Assert (S = '', 'ConvertLower');
  7558.     S := 'abc';
  7559.     ConvertLower (S);
  7560.     Assert (S = 'abc', 'ConvertLower');
  7561.     Assert (IsEqualNoCase ('@ABCDEFGHIJKLMNOPQRSTUVWXYZ` ', '@abcdefghijklmnopqrstuvwxyz` '), 'IsEqualNoCase');
  7562.   End;
  7563.  
  7564. Procedure Test_Misc;
  7565. var S : String;
  7566.     Ch : Char;
  7567.     I : Integer;
  7568.   Begin
  7569.     { Dup                                                                  }
  7570.     Assert (Dup ('xy', 3) = 'xyxyxy', 'Dup');
  7571.     Assert (Dup ('', 3) = '', 'Dup');
  7572.     Assert (Dup ('a', 0) = '', 'Dup');
  7573.     Assert (Dup ('a', -1) = '', 'Dup');
  7574.     Ch := 'x';
  7575.     Assert (Dup (Ch, 6) = 'xxxxxx', 'Dup');
  7576.     Assert (Dup (Ch, 0) = '', 'Dup');
  7577.     Assert (Dup (Ch, -1) = '', 'Dup');
  7578.  
  7579.     { Trim                                                                 }
  7580.     Assert (TrimLeft ('   123   ') = '123   ', 'TrimLeft');
  7581.     Assert (TrimLeftStr ('   123   ', '  ') = ' 123   ', 'TrimLeft');
  7582.     Assert (TrimRight ('   123   ') = '   123', 'TrimRight');
  7583.     Assert (TrimRightStr ('   123   ', '  ') = '   123 ', 'TrimRight');
  7584.     Assert (Trim ('   123   ', [' ']) = '123', 'Trim');
  7585.     Assert (Trim ('', [' ']) = '', 'Trim');
  7586.     Assert (Trim ('X', [' ']) = 'X', 'Trim');
  7587.     Assert (TrimQuotes ('"123"') = '123', 'TrimQuotes');
  7588.     Assert (TrimQuotes ('"1""23"') = '1""23', 'TrimQuotes');
  7589.  
  7590.     { Pad                                                                  }
  7591.     Assert (PadLeft ('xxx', 'y', 6) = 'yyyxxx', 'PadLeft');
  7592.     Assert (PadLeft ('xxx', 'y', 2, True) = 'xx', 'PadLeft');
  7593.     Assert (PadRight ('xxx', 'y', 6) = 'xxxyyy', 'PadRight');
  7594.     Assert (PadRight ('xxx', 'y', 2, True) = 'xx', 'PadRight');
  7595.     Assert (Pad ('xxx', 'y', 7) = 'yyxxxyy', 'Pad');
  7596.     Assert (Pad (123, 8) = '00000123', 'Pad');
  7597.     Assert (Pad (0, 1) = '0', 'Pad');
  7598.     Assert (Pad (0, 0, True) = '', 'Pad');
  7599.     Assert (Pad (0, 0) = '0', 'Pad');
  7600.     Assert (PadLeft ('x', ' ', 3, True) = '  x', 'PadLeft');
  7601.     Assert (PadLeft ('xabc', ' ', 3, True) = 'xab', 'PadLeft');
  7602.  
  7603.     { Paste                                                                }
  7604.     S := '1234567890';
  7605.     I := 1;
  7606.     Paste ('2', S, I, False);
  7607.     Assert (S = '2234567890', 'Paste');
  7608.     Paste ('012', S, I, False, 2, 3);
  7609.     Assert (S = '2124567890', 'Paste');
  7610.     Paste ('0', S, I, True);
  7611.     Assert (S = '2120567890', 'Paste');
  7612.     Paste ('0', S, I, True);
  7613.     Assert (S = '2100567890', 'Paste');
  7614.     Paste ('12', S, I, False);
  7615.     Assert (S = '2120567890', 'Paste');
  7616.  
  7617.     { Type checking                                                        }
  7618.     Assert (IsNumber ('1234567890'), 'IsNumber');
  7619.     Assert (IsInteger ('-1234567890'), 'IsInteger');
  7620.     Assert (IsReal ('-1234.567890'), 'IsReal');
  7621.     Assert (IsQuotedString ('"ABC""D"'), 'IsQuotedString');
  7622.     Assert (IsQuotedString ('"A"'), 'IsQuotedString');
  7623.     Assert (not IsQuotedString ('"ABC""D'''), 'IsQuotedString');
  7624.     Assert (not IsQuotedString ('"ABC""D'), 'IsQuotedString');
  7625.     Assert (not IsQuotedString ('"'), 'IsQuotedString');
  7626.     Assert (not IsQuotedString (''), 'IsQuotedString');
  7627.     Assert (IsQuotedString (''''''), 'IsQuotedString');
  7628.     Assert (not IsQuotedString ('''a'''''), 'IsQuotedString');
  7629.     Assert (UnQuoteText ('"123"') = '123', 'UnQuoteText');
  7630.     Assert (UnQuoteText ('"1""23"') = '1"23', 'UnQuoteText');
  7631.  
  7632.     { Reverse                                                              }
  7633.     Assert (Reversed ('12345') = '54321', 'Reverse');
  7634.     Assert (Reversed ('1234') = '4321', 'Reverse');
  7635.  
  7636.     { Join / Split                                                         }
  7637.     Assert (Join (Split ('x yy zzz', ' ')) = 'x yy zzz', 'Join/Split');
  7638.     Assert (Join (Split (' x  yy  zzz ', ' ')) = ' x  yy  zzz ', 'Join/Split');
  7639.  
  7640.     { CharClassStr                                                         }
  7641.     Assert (CharSetToCharClassStr (['a'..'z']) = '[a-z]', 'CharClassStr');
  7642.     Assert (CharSetToCharClassStr (CompleteCharSet) = '.', 'CharClassStr');
  7643.     Assert (CharSetToCharClassStr ([#0..#31]) = '[\x0-\x1F]', 'CharClassStr');
  7644.     Assert (CharSetToCharClassStr ([#0..#32]) = '[\x0- ]', 'CharClassStr');
  7645.     Assert (CharSetToCharClassStr (CompleteCharSet - ['a']) = '[^a]', 'CharClassStr');
  7646.     Assert (CharSetToCharClassStr (CompleteCharSet - ['a'..'z']) = '[^a-z]', 'CharClassStr');
  7647.     Assert (CharSetToCharClassStr (['a'..'b']) = '[ab]', 'CharClassStr');
  7648.     Assert (CharSetToCharClassStr ([]) = '[]', 'CharClassStr');
  7649.  
  7650.     Assert (CharClassStrToCharSet ('[a]') = ['a'], 'CharClassStr');
  7651.     Assert (CharClassStrToCharSet ('[]') = [], 'CharClassStr');
  7652.     Assert (CharClassStrToCharSet ('.') = CompleteCharSet, 'CharClassStr');
  7653.     Assert (CharClassStrToCharSet ('') = [], 'CharClassStr');
  7654.     Assert (CharClassStrToCharSet ('[a-z]') = ['a'..'z'], 'CharClassStr');
  7655.     Assert (CharClassStrToCharSet ('[^a-z]') = CompleteCharSet - ['a'..'z'], 'CharClassStr');
  7656.     Assert (CharClassStrToCharSet ('[-]') = ['-'], 'CharClassStr');
  7657.     Assert (CharClassStrToCharSet ('[a-]') = ['a', '-'], 'CharClassStr');
  7658.     Assert (CharClassStrToCharSet ('[\x5]') = [#$5], 'CharClassStr');
  7659.     Assert (CharClassStrToCharSet ('[\x1f]') = [#$1f], 'CharClassStr');
  7660.     Assert (CharClassStrToCharSet ('[\x10-]') = [#$10, '-'], 'CharClassStr');
  7661.     Assert (CharClassStrToCharSet ('[\x10-\x1f]') = [#$10..#$1f], 'CharClassStr');
  7662.     Assert (CharClassStrToCharSet ('[\x10-\xf]') = [], 'CharClassStr');
  7663.  
  7664.     { Ensure                                                               }
  7665.     S := 'ABC';
  7666.     EnsurePrefix (S, '\');
  7667.     Assert (S = '\ABC', 'EnsurePrefix');
  7668.     EnsureSuffix (S, '\');
  7669.     Assert (S = '\ABC\', 'EnsureSuffix');
  7670.     EnsureNoPrefix (S, '\');
  7671.     Assert (S = 'ABC\', 'EnsureNoPrefix');
  7672.     EnsureNoSuffix (S, '\');
  7673.     Assert (S = 'ABC', 'EnsureNoSuffix');
  7674.   End;
  7675.  
  7676. Procedure Test_RegEx;
  7677. var I : Integer;
  7678.   Begin
  7679.     { MatchPattern                                                         }
  7680.     Assert (MatchPattern ('a*b', 'ab'), 'MatchPattern');
  7681.     Assert (MatchPattern ('a*b', 'aab'), 'MatchPattern');
  7682.     Assert (MatchPattern ('a*b', 'accb'), 'MatchPattern');
  7683.     Assert (not MatchPattern ('a*b', 'a'), 'MatchPattern');
  7684.     Assert (MatchPattern ('a?b', 'acb'), 'MatchPattern');
  7685.     Assert (not MatchPattern ('a?b', 'ab'), 'MatchPattern');
  7686.     Assert (MatchPattern ('a[^a]', 'ab'), 'MatchPattern');
  7687.     Assert (MatchPattern ('a[0-9a-z]', 'ab'), 'MatchPattern');
  7688.     Assert (MatchPattern ('', ''), 'MatchPattern');
  7689.     Assert (not MatchPattern ('', 'a'), 'MatchPattern');
  7690.     Assert (not MatchPattern ('a', ''), 'MatchPattern');
  7691.     Assert (not MatchPattern ('?', ''), 'MatchPattern');
  7692.  
  7693.     { MatchQuantSeq                                                        }
  7694.     Assert (MatchQuantSeq (I, [cs_Alpha], [mqOnce], 'a', []));
  7695.     Assert (I = 1);
  7696.     Assert (MatchQuantSeq (I, [cs_Alpha], [mqAny], 'a', []));
  7697.     Assert (I = 1);
  7698.     Assert (MatchQuantSeq (I, [cs_Alpha], [mqLeastOnce], 'a', []));
  7699.     Assert (I = 1);
  7700.     Assert (MatchQuantSeq (I, [cs_Alpha], [mqOptional], 'a', []));
  7701.     Assert (I = 1);
  7702.  
  7703.     Assert (MatchQuantSeq (I, [cs_Alpha], [mqOnce], 'ab', []));
  7704.     Assert (I = 1);
  7705.     Assert (MatchQuantSeq (I, [cs_Alpha], [mqAny], 'ab', []));
  7706.     Assert (I = 2);
  7707.     Assert (MatchQuantSeq (I, [cs_Alpha], [mqLeastOnce], 'ab', []));
  7708.     Assert (I = 2);
  7709.     Assert (MatchQuantSeq (I, [cs_Alpha], [mqOptional], 'ab', []));
  7710.     Assert (I = 1);
  7711.  
  7712.     Assert (MatchQuantSeq (I, [cs_Alpha], [mqOnce], 'abc', []));
  7713.     Assert (I = 1);
  7714.     Assert (MatchQuantSeq (I, [cs_Alpha], [mqAny], 'abc', []));
  7715.     Assert (I = 3);
  7716.     Assert (MatchQuantSeq (I, [cs_Alpha], [mqLeastOnce], 'abc', []));
  7717.     Assert (I = 3);
  7718.     Assert (MatchQuantSeq (I, [cs_Alpha], [mqOptional], 'abc', []));
  7719.     Assert (I = 1);
  7720.  
  7721.     Assert (not MatchQuantSeq (I, [cs_Alpha, cs_Numeric], [mqOnce, mqOnce], 'ab12', []));
  7722.     Assert (I = 0);
  7723.     Assert (MatchQuantSeq (I, [cs_Alpha, cs_Numeric], [mqAny, mqOnce], 'abc123', []));
  7724.     Assert (I = 4);
  7725.     Assert (not MatchQuantSeq (I, [cs_Alpha, cs_Numeric], [mqLeastOnce, mqAny], '123', []));
  7726.     Assert (I = 0);
  7727.     Assert (MatchQuantSeq (I, [cs_Alpha, cs_Numeric], [mqOptional, mqAny], '123abc', []));
  7728.     Assert (I = 3);
  7729.     Assert (MatchQuantSeq (I, [cs_Alpha, cs_Numeric], [mqOnce, mqAny], 'a123', []));
  7730.     Assert (I = 4);
  7731.     Assert (MatchQuantSeq (I, [cs_Alpha, cs_Numeric], [mqAny, mqAny], 'abc123', []));
  7732.     Assert (I = 6);
  7733.     Assert (MatchQuantSeq (I, [cs_Alpha, cs_Numeric], [mqLeastOnce, mqOnce], 'ab123', []));
  7734.     Assert (I = 3);
  7735.     Assert (MatchQuantSeq (I, [cs_Alpha, cs_Numeric], [mqOptional, mqOptional], '1', []));
  7736.     Assert (I = 1);
  7737.     Assert (MatchQuantSeq (I, [cs_Alpha, cs_Numeric], [mqOptional, mqOptional], 'a', []));
  7738.     Assert (I = 1);
  7739.     Assert (MatchQuantSeq (I, [cs_Alpha, cs_Numeric], [mqOnce, mqOptional], 'ab', []));
  7740.     Assert (I = 1);
  7741.     Assert (not MatchQuantSeq (I, [cs_Alpha, cs_Numeric], [mqOptional, mqOnce], 'ab', []));
  7742.     Assert (I = 0);
  7743.  
  7744.     Assert (MatchQuantSeq (I, [cs_AlphaNumeric, cs_Numeric, cs_Alpha, cs_Numeric],
  7745.                             [mqLeastOnce, mqAny, mqOptional, mqOnce], 'a1b2', []));
  7746.     Assert (I = 4);
  7747.     Assert (MatchQuantSeq (I, [cs_AlphaNumeric, cs_Numeric, cs_Alpha, cs_Numeric],
  7748.                             [mqAny, mqOnce, mqOptional, mqOnce], 'a1b2cd3efg4', []));
  7749.     Assert (I = 4);
  7750.  
  7751.     Assert (MatchQuantSeq (I, [cs_AlphaNumeric, cs_Numeric], [mqAny, mqOptional], 'a1', [moDeterministic]));
  7752.     Assert (I = 2);
  7753.     Assert (not MatchQuantSeq (I, [cs_AlphaNumeric, cs_Numeric], [mqAny, mqOnce], 'a1', [moDeterministic]));
  7754.     Assert (I = 0);
  7755.     Assert (MatchQuantSeq (I, [cs_Alpha, cs_Numeric, cs_Alpha, cs_AlphaNumeric],
  7756.                             [mqAny, mqOnce, mqAny, mqLeastOnce], 'a1b2cd3efg4', [moDeterministic]));
  7757.     Assert (I = 11);
  7758.  
  7759.     Assert (MatchQuantSeq (I, [cs_AlphaNumeric, cs_Numeric], [mqAny, mqOptional], 'a1', [moNonGreedy]));
  7760.     Assert (I = 0);
  7761.     Assert (MatchQuantSeq (I, [cs_AlphaNumeric, cs_Numeric], [mqAny, mqLeastOnce], 'a1', [moNonGreedy]));
  7762.     Assert (I = 2);
  7763.     Assert (not MatchQuantSeq (I, [cs_AlphaNumeric, cs_Numeric], [mqAny, mqOnce], 'abc', [moNonGreedy]));
  7764.     Assert (I = 0);
  7765.     Assert (MatchQuantSeq (I, [cs_AlphaNumeric, cs_Numeric, cs_Alpha, cs_Numeric],
  7766.                             [mqAny, mqOnce, mqOnce, mqLeastOnce], 'a1bc2de3g4', [moNonGreedy]));
  7767.     Assert (I = 10);
  7768.   End;
  7769.  
  7770. Procedure SelfTest;
  7771.   Begin
  7772.     Test_Case;
  7773.     Test_Replace;
  7774.     Test_Copy;
  7775.     Test_Match;
  7776.     Test_CopyEx;
  7777.     Test_CopyDelim;
  7778.     Test_Pos;
  7779.     Test_PosEx;
  7780.     Test_Count;
  7781.     Test_Misc;
  7782.     Test_RegEx;
  7783.   End;
  7784.  
  7785.  
  7786.  
  7787. end.
  7788.  
  7789.