/[projects]/dao/DelphiScanner/Components/tpsystools_4.04/source/StStrS.pas
ViewVC logotype

Annotation of /dao/DelphiScanner/Components/tpsystools_4.04/source/StStrS.pas

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2671 - (hide annotations) (download)
Tue Aug 25 18:15:15 2015 UTC (8 years, 10 months ago) by torben
File size: 100553 byte(s)
Added tpsystools component
1 torben 2671 (* ***** BEGIN LICENSE BLOCK *****
2     * Version: MPL 1.1
3     *
4     * The contents of this file are subject to the Mozilla Public License Version
5     * 1.1 (the "License"); you may not use this file except in compliance with
6     * the License. You may obtain a copy of the License at
7     * http://www.mozilla.org/MPL/
8     *
9     * Software distributed under the License is distributed on an "AS IS" basis,
10     * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11     * for the specific language governing rights and limitations under the
12     * License.
13     *
14     * The Original Code is TurboPower SysTools
15     *
16     * The Initial Developer of the Original Code is
17     * TurboPower Software
18     *
19     * Portions created by the Initial Developer are Copyright (C) 1996-2002
20     * the Initial Developer. All Rights Reserved.
21     *
22     * Contributor(s):
23     *
24     * ***** END LICENSE BLOCK ***** *)
25    
26     {*********************************************************}
27     {* SysTools: StStrS.pas 4.04 *}
28     {*********************************************************}
29     {* SysTools: Short string routines *}
30     {*********************************************************}
31    
32     {$I StDefine.inc}
33    
34     unit StStrS;
35    
36     interface
37    
38     uses
39     Windows,
40     Classes,
41     SysUtils,
42     StConst,
43     StBase;
44    
45     {-------- Numeric conversion -----------}
46    
47     function HexBS(B : Byte) : ShortString;
48     {-Return the hex string for a byte.}
49    
50     function HexWS(W : Word) : ShortString;
51     {-Return the hex string for a word.}
52    
53     function HexLS(L : LongInt) : ShortString;
54     {-Return the hex string for a long integer.}
55    
56     function HexPtrS(P : Pointer) : ShortString;
57     {-Return the hex string for a pointer.}
58    
59     function BinaryBS(B : Byte) : ShortString;
60     {-Return a binary string for a byte.}
61    
62     function BinaryWS(W : Word) : ShortString;
63     {-Return the binary string for a word.}
64    
65     function BinaryLS(L : LongInt) : ShortString;
66     {-Return the binary string for a long integer.}
67    
68     function OctalBS(B : Byte) : ShortString;
69     {-Return an octal string for a byte.}
70    
71     function OctalWS(W : Word) : ShortString;
72     {-Return an octal string for a word.}
73    
74     function OctalLS(L : LongInt) : ShortString;
75     {-Return an octal string for a long integer.}
76    
77     function Str2Int16S(const S : ShortString; var I : SmallInt) : Boolean;
78     {-Convert a string to an SmallInt.}
79    
80     function Str2WordS(const S : ShortString; var I : Word) : Boolean;
81     {-Convert a string to a word.}
82    
83     function Str2LongS(const S : ShortString; var I : LongInt) : Boolean;
84     {-Convert a string to a long integer.}
85    
86     {$IFDEF VER93}
87     function Str2RealS(const S : ShortString; var R : Double) : Boolean;
88     {$ELSE}
89     {-Convert a string to a real.}
90     function Str2RealS(const S : ShortString; var R : Real) : Boolean;
91     {$ENDIF}
92    
93     function Str2ExtS(const S : ShortString; var R : Extended) : Boolean;
94     {-Convert a string to an extended.}
95    
96     function Long2StrS(L : LongInt) : ShortString;
97     {-Convert an integer type to a string.}
98    
99     function Real2StrS(R : Double; Width : Byte; Places : ShortInt) : ShortString;
100     {-Convert a real to a string.}
101    
102     function Ext2StrS(R : Extended; Width : Byte; Places : ShortInt) : ShortString;
103     {-Convert an extended to a string.}
104    
105     function ValPrepS(const S : ShortString) : ShortString;
106     {-Prepares a string for calling Val.}
107    
108    
109     {-------- General purpose string manipulation --------}
110    
111     function CharStrS(C : AnsiChar; Len : Cardinal) : ShortString;
112     {-Return a string filled with the specified character.}
113    
114     function PadChS(const S : ShortString; C : AnsiChar; Len : Cardinal) : ShortString;
115     {-Pad a string on the right with a specified character.}
116    
117     function PadS(const S : ShortString; Len : Cardinal) : ShortString;
118     {-Pad a string on the right with spaces.}
119    
120     function LeftPadChS(const S : ShortString; C : AnsiChar; Len : Cardinal) : ShortString;
121     {-Pad a string on the left with a specified character.}
122    
123     function LeftPadS(const S : ShortString; Len : Cardinal) : ShortString;
124     {-Pad a string on the left with spaces.}
125    
126     function TrimLeadS(const S : ShortString) : ShortString;
127     {-Return a string with leading white space removed.}
128    
129     function TrimTrailS(const S : ShortString) : ShortString;
130     {-Return a string with trailing white space removed.}
131    
132     function TrimS(const S : ShortString) : ShortString;
133     {-Return a string with leading and trailing white space removed.}
134    
135     function TrimSpacesS(const S : ShortString) : ShortString;
136     {-Return a string with leading and trailing spaces removed.}
137    
138     function CenterChS(const S : ShortString; C : AnsiChar; Len : Cardinal) : ShortString;
139     {-Pad a string on the left and right with a specified character.}
140    
141     function CenterS(const S : ShortString; Len : Cardinal) : ShortString;
142     {-Pad a string on the left and right with spaces.}
143    
144     function EntabS(const S : ShortString; TabSize : Byte) : ShortString;
145     {-Convert blanks in a string to tabs.}
146    
147     function DetabS(const S : ShortString; TabSize : Byte) : ShortString;
148     {-Expand tabs in a string to blanks.}
149    
150     function ScrambleS(const S, Key : ShortString) : ShortString;
151     {-Encrypt / Decrypt string with enhanced XOR encryption.}
152    
153     function SubstituteS(const S, FromStr, ToStr : ShortString) : ShortString;
154     {-Map the characters found in FromStr to the corresponding ones in ToStr.}
155    
156     function FilterS(const S, Filters : ShortString) : ShortString;
157     {-Remove characters from a string. The characters to remove are specified in
158     ChSet.}
159    
160     {--------------- Word / Char manipulation -------------------------}
161    
162     function CharExistsS(const S : ShortString; C : AnsiChar) : Boolean; overload;
163     function CharExistsS(const S : String; C : Char) : Boolean; overload;
164     {-Determines whether a given character exists in a string. }
165    
166     function CharCountS(const S : ShortString; C : AnsiChar) : Byte;
167     {-Count the number of a given character in a string. }
168    
169     function WordCountS(const S, WordDelims : ShortString) : Cardinal;
170     {-Given an array of word delimiters, return the number of words in a string.}
171    
172     function WordPositionS(N : Cardinal; const S, WordDelims : ShortString;
173     var Pos : Cardinal) : Boolean;
174     {-Given an array of word delimiters, set Pos to the start position of the
175     N'th word in a string. Result indicates success/failure.}
176    
177     function ExtractWordS(N : Cardinal; const S, WordDelims : ShortString) : ShortString;
178     {-Given an array of word delimiters, return the N'th word in a string.}
179    
180     function AsciiCountS(const S, WordDelims : ShortString; Quote : AnsiChar) : Cardinal;
181     {-Return the number of words in a string.}
182    
183     function AsciiPositionS(N : Cardinal; const S, WordDelims : ShortString;
184     Quote : AnsiChar; var Pos : Cardinal) : Boolean;
185     {-Return the position of the N'th word in a string.}
186    
187     function ExtractAsciiS(N : Cardinal; const S, WordDelims : ShortString;
188     Quote : AnsiChar) : ShortString;
189     {-Given an array of word delimiters, return the N'th word in a string. Any
190     text within Quote characters is counted as one word.}
191    
192     procedure WordWrapS(const InSt : ShortString; var OutSt, Overlap : ShortString;
193     Margin : Cardinal; PadToMargin : Boolean);
194     {-Wrap a text string at a specified margin.}
195    
196     {--------------- String comparison and searching -----------------}
197     function CompStringS(const S1, S2 : ShortString) : Integer;
198     {-Compare two strings.}
199    
200     function CompUCStringS(const S1, S2 : ShortString) : Integer;
201     {-Compare two strings. This compare is not case sensitive.}
202    
203     function SoundexS(const S : ShortString) : ShortString;
204     {-Return 4 character soundex of an input string.}
205    
206     function MakeLetterSetS(const S : ShortString) : Longint;
207     {-Return a bit-mapped long storing the individual letters contained in S.}
208    
209     procedure BMMakeTableS(const MatchString : ShortString; var BT : BTable);
210     {-Build a Boyer-Moore link table}
211    
212     function BMSearchS(var Buffer; BufLength : Cardinal; var BT : BTable;
213     const MatchString : ShortString ; var Pos : Cardinal) : Boolean;
214     {-Use the Boyer-Moore search method to search a buffer for a string.}
215    
216     function BMSearchUCS(var Buffer; BufLength : Cardinal; var BT : BTable;
217     const MatchString : ShortString ; var Pos : Cardinal) : Boolean;
218     {-Use the Boyer-Moore search method to search a buffer for a string. This
219     search is not case sensitive.}
220    
221     {--------------- DOS pathname parsing -----------------}
222    
223     function DefaultExtensionS(const Name, Ext : ShortString) : ShortString;
224     {-Return a file name with a default extension attached.}
225    
226     function ForceExtensionS(const Name, Ext : ShortString) : ShortString;
227     {-Force the specified extension onto the file name.}
228    
229     function JustFilenameS(const PathName : ShortString) : ShortString;
230     {-Return just the filename and extension of a pathname.}
231    
232     function JustNameS(const PathName : ShortString) : ShortString;
233     {-Return just the filename (no extension, path, or drive) of a pathname.}
234    
235     function JustExtensionS(const Name : ShortString) : ShortString;
236     {-Return just the extension of a pathname.}
237    
238     function JustPathnameS(const PathName : ShortString) : ShortString;
239     {-Return just the drive and directory portion of a pathname.}
240    
241     function AddBackSlashS(const DirName : ShortString) : ShortString;
242     {-Add a default backslash to a directory name.}
243    
244     function CleanPathNameS(const PathName : ShortString) : ShortString;
245     {-Return a pathname cleaned up as DOS does it.}
246    
247     function HasExtensionS(const Name : ShortString; var DotPos : Cardinal) : Boolean;
248     {-Determine if a pathname contains an extension and, if so, return the
249     position of the dot in front of the extension.}
250    
251     {------------------ Formatting routines --------------------}
252    
253     function CommaizeS(L : LongInt) : ShortString;
254     {-Convert a long integer to a string with commas.}
255    
256     function CommaizeChS(L : Longint; Ch : AnsiChar) : ShortString;
257     {-Convert a long integer to a string with Ch in comma positions.}
258    
259     function FloatFormS(const Mask : ShortString ; R : TstFloat ; const LtCurr,
260     RtCurr : ShortString ; Sep, DecPt : AnsiChar) : ShortString;
261     {-Return a formatted string with digits from R merged into mask.}
262    
263     function LongIntFormS(const Mask : ShortString ; L : LongInt ; const LtCurr,
264     RtCurr : ShortString ; Sep : AnsiChar) : ShortString;
265     {-Return a formatted string with digits from L merged into mask.}
266    
267     function StrChPosS(const P : string; C : Char; var Pos : Cardinal) : Boolean; overload;
268     function StrChPosS(const P : ShortString; C : AnsiChar; var Pos : Cardinal) : Boolean; overload;
269    
270     {-Return the position of a specified character within a string.}
271    
272     function StrStPosS(const P, S : ShortString; var Pos : Cardinal) : Boolean;
273     {-Return the position of a specified substring within a string.}
274    
275     function StrStCopyS(const S : ShortString; Pos, Count : Cardinal) : ShortString;
276     {-Copy characters at a specified position in a string.}
277    
278     function StrChInsertS(const S : ShortString; C : AnsiChar; Pos : Cardinal) : ShortString;
279     {-Insert a character into a string at a specified position.}
280    
281     function StrStInsertS(const S1, S2 : ShortString; Pos : Cardinal) : ShortString;
282     {-Insert a string into another string at a specified position.}
283    
284     function StrChDeleteS(const S : ShortString; Pos : Cardinal) : ShortString;
285     {-Delete the character at a specified position in a string.}
286    
287     function StrStDeleteS(const S : ShortString; Pos, Count : Cardinal) : ShortString;
288     {-Delete characters at a specified position in a string.}
289    
290    
291     {-------------------------- New Functions -----------------------------------}
292    
293     function ContainsOnlyS(const S, Chars : ShortString;
294     var BadPos : Cardinal) : Boolean;
295    
296     function ContainsOtherThanS(const S, Chars : ShortString;
297     var BadPos : Cardinal) : Boolean;
298    
299     function CopyLeftS(const S : ShortString; Len : Cardinal) : ShortString;
300     {-Return the left Len characters of a string}
301    
302     function CopyMidS(const S : ShortString; First, Len : Cardinal) : ShortString;
303     {-Return the mid part of a string}
304    
305     function CopyRightS(const S : ShortString; First : Cardinal) : ShortString;
306     {-Return the right Len characters of a string}
307    
308     function CopyRightAbsS(const S : ShortString; NumChars : Cardinal) : ShortString;
309     {-Return NumChar characters starting from end}
310    
311     function CopyFromNthWordS(const S, WordDelims : ShortString;
312     const AWord : ShortString; N : Cardinal; {!!.02}
313     var SubString : ShortString) : Boolean;
314    
315     function DeleteFromNthWordS(const S, WordDelims : ShortString;
316     AWord : ShortString; N : Cardinal;
317     var SubString : ShortString) : Boolean;
318    
319     function CopyFromToWordS(const S, WordDelims, Word1, Word2 : ShortString;
320     N1, N2 : Cardinal;
321     var SubString : ShortString) : Boolean;
322    
323     function DeleteFromToWordS(const S, WordDelims, Word1, Word2 : ShortString;
324     N1, N2 : Cardinal;
325     var SubString : ShortString) : Boolean;
326    
327     function CopyWithinS(const S, Delimiter : ShortString;
328     Strip : Boolean) : ShortString;
329    
330     function DeleteWithinS(const S, Delimiter : ShortString) : ShortString;
331    
332     function ExtractTokensS(const S, Delims : ShortString;
333     QuoteChar : AnsiChar;
334     AllowNulls : Boolean;
335     Tokens : TStrings) : Cardinal;
336    
337     function IsChAlphaS(C : Char) : Boolean;
338     {-Returns true if Ch is an alpha}
339    
340     function IsChNumericS(C : AnsiChar; const Numbers : ShortString) : Boolean;
341     {-Returns true if Ch in numeric set}
342    
343     function IsChAlphaNumericS(C : Char; const Numbers : ShortString) : Boolean;
344     {-Returns true if Ch is an alpha or numeric}
345    
346     function IsStrAlphaS(const S : string) : Boolean;
347     {-Returns true if all characters in string are an alpha}
348    
349     function IsStrNumericS(const S, Numbers : ShortString) : Boolean;
350     {-Returns true if all characters in string are in numeric set}
351    
352     function IsStrAlphaNumericS(const S, Numbers : String) : Boolean;
353     {-Returns true if all characters in string are alpha or numeric}
354    
355     function LastWordS(const S, WordDelims, AWord : ShortString;
356     var Position : Cardinal) : Boolean;
357     {-returns the position in a string of the last instance of a given word}
358    
359     function LastWordAbsS(const S, WordDelims : ShortString;
360     var Position : Cardinal) : Boolean;
361     {-returns the position in a string of the last word}
362    
363     function LastStringS(const S, AString : ShortString;
364     var Position : Cardinal) : Boolean;
365     {-returns the position in a string of the last instance of a given string}
366    
367     function LeftTrimCharsS(const S, Chars : ShortString) : ShortString;
368     {-strips given characters from the beginning of a string}
369    
370     function KeepCharsS(const S, Chars : ShortString) : ShortString;
371     {-returns a string containing only those characters in a given set}
372    
373     function RepeatStringS(const RepeatString : ShortString;
374     var Repetitions : Cardinal;
375     MaxLen : Cardinal) : ShortString;
376     {-creates a string of up to Repetition instances of a string}
377    
378     function ReplaceStringS(const S, OldString, NewString : ShortString;
379     N : Cardinal;
380     var Replacements : Cardinal) : ShortString;
381     {-replaces a substring with up to Replacements instances of a string}
382    
383     function ReplaceStringAllS(const S, OldString, NewString : ShortString;
384     var Replacements : Cardinal) : ShortString;
385     {-replaces all instances of a substring with one or more instances of a string}
386    
387     function ReplaceWordS(const S, WordDelims, OldWord, NewWord : ShortString;
388     N : Cardinal;
389     var Replacements : Cardinal) : ShortString;
390     {-replaces a given word with one or more instances of a string}
391    
392     function ReplaceWordAllS(const S, WordDelims, OldWord, NewWord : ShortString;
393     var Replacements : Cardinal) : ShortString;
394     {-replaces all instances of a word with one or more instances of a string}
395    
396     function RightTrimCharsS(const S, Chars : ShortString) : ShortString;
397     {-removes those characters at the end of a string contained in a set of characters}
398    
399     function StrWithinS(const S, SearchStr : ShortString;
400     Start : Cardinal;
401     var Position : Cardinal) : boolean;
402     {-finds the position of a substring within a string starting at a given point}
403    
404     function TrimCharsS(const S, Chars : ShortString) : ShortString;
405     {-removes trailing and leading characters defined by a string from a string}
406    
407     function WordPosS(const S, WordDelims, AWord : ShortString;
408     N : Cardinal; var Position : Cardinal) : Boolean;
409     {-returns the Nth instance of a word within a string}
410    
411    
412     implementation
413    
414    
415     {-------- Numeric conversion -----------}
416    
417     function HexBS(B : Byte) : ShortString;
418     {-Return the hex string for a byte.}
419     begin
420     Result[0] := #2;
421     Result[1] := StHexDigits[B shr 4];
422     Result[2] := StHexDigits[B and $F];
423     end;
424    
425     function HexWS(W : Word) : ShortString;
426     {-Return the hex string for a word.}
427     begin
428     Result[0] := #4;
429     Result[1] := StHexDigits[hi(W) shr 4];
430     Result[2] := StHexDigits[hi(W) and $F];
431     Result[3] := StHexDigits[lo(W) shr 4];
432     Result[4] := StHexDigits[lo(W) and $F];
433     end;
434    
435     function HexLS(L : LongInt) : ShortString;
436     {-Return the hex string for a long integer.}
437     begin
438     Result := HexWS(HiWord(DWORD(L))) + HexWS(LoWord(DWORD(L))); {!!.02}
439     end;
440    
441     function HexPtrS(P : Pointer) : ShortString;
442     {-Return the hex string for a pointer.}
443     begin
444     Result := HexLS(LongInt(P)); {!!.02}
445     end;
446    
447     function BinaryBS(B : Byte) : ShortString;
448     {-Return a binary string for a byte.}
449     var
450     I, N : Cardinal;
451     begin
452     N := 1;
453     Result[0] := #8;
454     for I := 7 downto 0 do begin
455     Result[N] := StHexDigits[Ord(B and (1 shl I) <> 0)]; {0 or 1}
456     Inc(N);
457     end;
458     end;
459    
460     function BinaryWS(W : Word) : ShortString;
461     {-Return the binary string for a word.}
462     var
463     I, N : Cardinal;
464     begin
465     N := 1;
466     Result[0] := #16;
467     for I := 15 downto 0 do begin
468     Result[N] := StHexDigits[Ord(W and (1 shl I) <> 0)]; {0 or 1}
469     Inc(N);
470     end;
471     end;
472    
473     function BinaryLS(L : LongInt) : ShortString;
474     {-Return the binary string for a long integer.}
475     var
476     I : Longint;
477     N : Byte;
478     begin
479     N := 1;
480     Result[0] := #32;
481     for I := 31 downto 0 do begin
482     Result[N] := StHexDigits[Ord(L and LongInt(1 shl I) <> 0)]; {0 or 1}
483     Inc(N);
484     end;
485     end;
486    
487     function OctalBS(B : Byte) : ShortString;
488     {-Return an octal string for a byte.}
489     var
490     I : Cardinal;
491     begin
492     Result[0] := #3;
493     for I := 0 to 2 do begin
494     Result[3-I] := StHexDigits[B and 7];
495     B := B shr 3;
496     end;
497     end;
498    
499     function OctalWS(W : Word) : ShortString;
500     {-Return an octal string for a word.}
501     var
502     I : Cardinal;
503     begin
504     Result[0] := #6;
505     for I := 0 to 5 do begin
506     Result[6-I] := StHexDigits[W and 7];
507     W := W shr 3;
508     end;
509     end;
510    
511     function OctalLS(L : LongInt) : ShortString;
512     {-Return an octal string for a long integer.}
513     var
514     I : Cardinal;
515     begin
516     Result[0] := #12;
517     for I := 0 to 11 do begin
518     Result[12-I] := StHexDigits[L and 7];
519     L := L shr 3;
520     end;
521     end;
522    
523     function Str2Int16S(const S : ShortString; var I : SmallInt) : Boolean;
524     {-Convert a string to an SmallInt.}
525    
526     var
527     ec : Integer;
528     begin
529     ValSmallint(S, I, ec);
530     if (ec = 0) then
531     Result := true
532     else begin
533     Result := false;
534     if (ec < 0) then
535     I := succ(length(S))
536     else
537     I := ec;
538     end;
539     end;
540    
541     function Str2WordS(const S : ShortString; var I : Word) : Boolean;
542     {-Convert a string to a word.}
543    
544     var
545     ec : Integer;
546     begin
547     ValWord(S, I, ec);
548     if (ec = 0) then
549     Result := true
550     else begin
551     Result := false;
552     if (ec < 0) then
553     I := succ(length(S))
554     else
555     I := ec;
556     end;
557     end;
558    
559     function Str2LongS(const S : ShortString; var I : LongInt) : Boolean;
560     {-Convert a string to a long integer.}
561    
562     var
563     ec : Integer;
564     begin
565     ValLongint(S, I, ec);
566     if (ec = 0) then
567     Result := true
568     else begin
569     Result := false;
570     if (ec < 0) then
571     I := succ(length(S))
572     else
573     I := ec;
574     end;
575     end;
576    
577     {$IFDEF VER93}
578     function Str2RealS(const S : ShortString; var R : Double) : Boolean;
579     {$ELSE}
580     {-Convert a string to a real.}
581     function Str2RealS(const S : ShortString; var R : Real) : Boolean;
582     {$ENDIF}
583     {-Convert a string to a real.}
584     var
585     Code : Integer;
586     St : ShortString;
587     SLen : Byte absolute St;
588     begin
589     St := S;
590     {trim trailing blanks}
591     while St[SLen] = ' ' do
592     Dec(SLen);
593     Val(ValPrepS(St), R, Code);
594     if Code <> 0 then begin
595     R := Code;
596     Result := False;
597     end else
598     Result := True;
599     end;
600    
601     function Str2ExtS(const S : ShortString; var R : Extended) : Boolean;
602     {-Convert a string to an extended.}
603     var
604     Code : Integer;
605     P : ShortString;
606     PLen : Byte absolute P;
607     begin
608     P := S;
609     {trim trailing blanks}
610     while P[PLen] = ' ' do
611     Dec(PLen);
612     Val(ValPrepS(P), R, Code);
613     if Code <> 0 then begin
614     R := Code;
615     Result := False;
616     end else
617     Result := True;
618     end;
619    
620     function Long2StrS(L : LongInt) : ShortString;
621     {-Convert an integer type to a string.}
622     begin
623     Str(L, Result);
624     end;
625    
626     function Real2StrS(R : Double; Width : Byte; Places : ShortInt) : ShortString;
627     {-Convert a real to a string.}
628     begin
629     Str(R:Width:Places, Result);
630     end;
631    
632     function Ext2StrS(R : Extended; Width : Byte; Places : ShortInt) : ShortString;
633     {-Convert an extended to a string.}
634     begin
635     Str(R:Width:Places, Result);
636     end;
637    
638     function ValPrepS(const S : ShortString) : ShortString;
639     {-Prepares a string for calling Val.}
640     var
641     P : Cardinal;
642     begin
643     Result := TrimSpacesS(S);
644     if Result <> '' then begin
645     if StrChPosS(Result, {$IFDEF DELPHIXE2}FormatSettings.{$ENDIF}DecimalSeparator, P) then begin
646     Result[P] := '.';
647     if P = Byte(Result[0]) then
648     Result[0] := AnsiChar(Pred(P));
649     end;
650     end else begin
651     Result := '0';
652     end;
653     end;
654    
655     {-------- General purpose string manipulation --------}
656    
657     function CharStrS(C : AnsiChar; Len : Cardinal) : ShortString;
658     {-Return a string filled with the specified character.}
659     begin
660     if Len = 0 then
661     Result[0] := #0
662     else begin
663     Result[0] := AnsiChar(Len);
664     FillChar(Result[1], Len, C);
665     end;
666     end;
667    
668     function PadChS(const S : ShortString; C : AnsiChar; Len : Cardinal) : ShortString;
669     {-Pad a string on the right with a specified character.}
670     var
671     SLen : Byte absolute S;
672     begin
673     if Length(S) >= Len then
674     Result := S
675     else begin
676     if Len > 255 then Len := 255;
677     Result[0] := AnsiChar(Len);
678     Move(S[1], Result[1], SLen);
679     if SLen < 255 then
680     FillChar(Result[Succ(SLen)], Len-SLen, C);
681     end;
682     end;
683    
684     function PadS(const S : ShortString; Len : Cardinal) : ShortString;
685     {-Pad a string on the right with spaces.}
686     begin
687     Result := PadChS(S, ' ', Len);
688     end;
689    
690     function LeftPadChS(const S : ShortString; C : AnsiChar; Len : Cardinal) : ShortString;
691     {-Pad a string on the left with a specified character.}
692     begin
693     if Length(S) >= Len then
694     Result := S
695     else if Length(S) < 255 then begin
696     if Len > 255 then Len := 255;
697     Result[0] := AnsiChar(Len);
698     Move(S[1], Result[Succ(Word(Len))-Length(S)], Length(S));
699     FillChar(Result[1], Len-Length(S), C);
700     end;
701     end;
702    
703     function LeftPadS(const S : ShortString; Len : Cardinal) : ShortString;
704     {-Pad a string on the left with spaces.}
705     begin
706     Result := LeftPadChS(S, ' ', Len);
707     end;
708    
709     function TrimLeadS(const S : ShortString) : ShortString;
710     {-Return a string with leading white space removed}
711     var
712     I : Cardinal;
713     begin
714     {!!.03 - added }
715     if S = '' then begin
716     Result := '';
717     Exit;
718     end;
719     {!!.03 - added end }
720     I := 1;
721     while (I <= Length(S)) and (S[I] <= ' ') do
722     Inc(I);
723     Move(S[I], Result[1], Length(S)-I+1);
724     Result[0] := AnsiChar(Length(S)-I+1);
725     end;
726    
727     function TrimTrailS(const S : ShortString) : ShortString;
728     {-Return a string with trailing white space removed.}
729     begin
730     Result := S;
731     while (Length(Result) > 0) and (Result[Length(Result)] <= ' ') do
732     Dec(Result[0]);
733     end;
734    
735     function TrimS(const S : ShortString) : ShortString;
736     {-Return a string with leading and trailing white space removed.}
737     var
738     I : Cardinal;
739     SLen : Byte absolute Result;
740     begin
741     Result := S;
742     while (SLen > 0) and (Result[SLen] <= ' ') do
743     Dec(SLen);
744    
745     I := 1;
746     while (I <= SLen) and (Result[I] <= ' ') do
747     Inc(I);
748     Dec(I);
749     if I > 0 then
750     Delete(Result, 1, I);
751     end;
752    
753     function TrimSpacesS(const S : ShortString) : ShortString;
754     {-Return a string with leading and trailing spaces removed.}
755     var
756     I : Word;
757     begin
758     Result := S;
759     while (Length(Result) > 0) and (Result[Length(Result)] = ' ') do
760     Dec(Result[0]);
761     I := 1;
762     while (I <= Length(Result)) and (S[I] = ' ') do
763     Inc(I);
764     Dec(I);
765     if I > 0 then
766     Delete(Result, 1, I);
767     end;
768    
769     function CenterChS(const S : ShortString; C : AnsiChar; Len : Cardinal) : ShortString;
770     {-Pad a string on the left and right with a specified character.}
771     begin
772     if Length(S) >= Len then
773     Result := S
774     else if Length(S) < 255 then begin
775     if Len > 255 then Len := 255;
776     Result[0] := AnsiChar(Len);
777     FillChar(Result[1], Len, C);
778     Move(S[1], Result[Succ((Len-Length(S)) shr 1)], Length(S));
779     end;
780     end;
781    
782     function CenterS(const S : ShortString; Len : Cardinal) : ShortString;
783     {-Pad a string on the left and right with spaces.}
784     begin
785     Result := CenterChS(S, ' ', Len);
786     end;
787    
788     function EntabS(const S : ShortString; TabSize : Byte) : ShortString;
789     {-Convert blanks in a string to tabs.}
790     register;
791     asm
792     push ebx { Save registers }
793     push edi
794     push esi
795    
796     mov esi, eax { ESI => input string }
797     mov edi, ecx { EDI => output string }
798     xor ebx, ebx { Initial SpaceCount = 0 }
799     xor ecx, ecx { Default input length = 0 }
800     and edx, 0FFh { Default output length = 0 in DH, TabSize in DL }
801    
802     mov cl, [esi] { Get input length }
803     inc esi
804     or edx, edx { TabSize = 0? }
805     jnz @@DefLength
806     mov ecx, edx { Return zero length string if TabSize = 0 }
807    
808     @@DefLength:
809     mov [edi], cl { Store default output length }
810     inc edi
811     or ecx, ecx
812     jz @@Done { Done if empty input string }
813     inc ch { Current input position=1 }
814    
815     @@Next:
816     or ebx, ebx { Compare SpaceCount to 0 }
817     jz @@NoTab { If SpaceCount=0 then no tab insert here }
818     xor eax, eax
819     mov al, ch { Ipos to AL }
820     div dl { Ipos DIV TabSize }
821     cmp ah, 1 { Ipos MOD TabSize = 1 ? }
822     jnz @@NoTab { If not, no tab insert here }
823     sub edi, ebx { Remove unused characters from output string }
824     sub dh, bl { Reduce Olen by SpaceCount }
825     inc dh { Add one to output length }
826     xor ebx, ebx { Reset SpaceCount }
827     mov byte ptr [edi], 09h { Store a tab }
828     inc edi
829    
830     @@NoTab:
831     mov al, [esi] { Get next input character }
832     inc esi
833     cmp cl, ch { End of string? }
834     jz @@Store { Yes, store character anyway }
835     inc bl { Increment SpaceCount }
836     cmp al, 32 { Is character a space? }
837     jz @@Store { Yes, store it for now }
838     xor ebx, ebx { Reset SpaceCount }
839     cmp al, 39 { Is it a quote? }
840     jz @@Quotes { Yep, enter quote loop }
841     cmp al, 34 { Is it a doublequote? }
842     jnz @@Store { Nope, store it }
843    
844     @@Quotes:
845     mov ah, al { Save quote start }
846    
847     @@NextQ:
848     mov [edi], al { Store quoted character }
849     inc edi
850     inc dh { Increment output length }
851     mov al, [esi] { Get next character }
852     inc esi
853     inc ch { Increment Ipos }
854     cmp ch, cl { At end of line? }
855     jae @@Store { If so, exit quote loop }
856     cmp al, ah { Matching end quote? }
857     jnz @@NextQ { Nope, stay in quote loop }
858     cmp al, 39 { Single quote? }
859     jz @@Store { Exit quote loop }
860     cmp byte ptr [esi-2],'\'{ Previous character an escape? }
861     jz @@NextQ { Stay in if so }
862    
863     @@Store:
864     mov [edi], al { Store last character }
865     inc edi
866     inc dh { Increment output length }
867     inc ch { Increment input position }
868     jz @@StoreLen { Exit if past 255 }
869     cmp ch, cl { Compare Ipos to Ilen }
870     jbe @@Next { Repeat while characters left }
871    
872     @@StoreLen:
873     xor eax, eax
874     mov al, dh
875     sub edi, eax
876     dec edi
877     mov [edi], dh { Store final length }
878    
879     @@Done:
880     pop esi
881     pop edi
882     pop ebx
883     end;
884    
885     function DetabS(const S : ShortString; TabSize : Byte) : ShortString;
886     {-Expand tabs in a string to blanks.}
887     register;
888     asm
889     push ebx
890     push edi
891     push esi
892    
893     mov edi, ecx { EDI => output string }
894     mov esi, eax { ESI => input string }
895     xor ecx, ecx { Default input length = 0 }
896     and edx, 0FFh { Default output length = 0 in DH, DL is Tabsize }
897     xor eax, eax
898     mov cl, [esi] { Get input length }
899     inc esi
900     or edx, edx { TabSize = 0? }
901     jnz @@DefLength
902     mov ecx, edx { Return zero length string if TabSize = 0 }
903    
904     @@DefLength:
905     mov [edi], cl { Store default output length }
906     inc edi
907     or ecx, ecx
908     jz @@Done { Done if empty input string }
909     mov ah, 09h { Store tab in AH }
910     mov bl, 255 { Maximum length of output }
911    
912     @@Next:
913     mov al, [esi] { Next input character }
914     inc esi
915     cmp al, ah { Is it a tab? }
916     jz @@Tab { Yes, compute next tab stop }
917     mov [edi], al { No, store to output }
918     inc edi
919     inc dh { Increment output length }
920     cmp dh, bl { 255 characters max }
921     jz @@StoreLen
922     dec cl
923     jnz @@Next { Next character while Olen <= 255 }
924     jmp @@StoreLen { Loop termination }
925    
926     @@Tab:
927     mov bh, cl { Save input counter }
928     mov al, dh { Current output length in AL }
929     and eax, 0FFh { Clear top byte }
930     div dl { OLen DIV TabSize in AL }
931     inc al { Round up to next tab position }
932     mul dl { Next tab position in AX }
933     or ah, ah { AX > 255? }
934     jnz @@StoreLen { Can't store it }
935     sub al, dh { Count of blanks to insert }
936     add dh, al { New output length in DH }
937     mov cl, al { Loop counter for blanks }
938     mov ax, 0920h { Tab in AH, Blank in AL }
939     rep stosb { Store blanks }
940     mov cl, bh { Restore input position }
941     dec cl
942     jnz @@Next { Back for next input }
943    
944     @@StoreLen:
945     xor eax, eax
946     mov al, dh
947     sub edi, eax
948     dec edi
949     mov [edi], dh { Store final length }
950    
951     @@Done:
952     pop esi
953     pop edi
954     pop ebx
955     end;
956    
957     function ScrambleS(const S, Key : ShortString) : ShortString;
958     {-Encrypt / Decrypt string with enhanced XOR encryption.}
959     var
960     J, LKey, LStr : Byte;
961     I : Cardinal;
962     begin
963     Result := S;
964     LKey := Length(Key);
965     LStr := Length(S);
966     if LKey = 0 then Exit;
967     if LStr = 0 then Exit;
968     I := 1;
969     J := LKey;
970     while I <= LStr do begin
971     if J = 0 then
972     J := LKey;
973     if (S[I] <> Key[J]) then
974     Result[I] := AnsiChar(Byte(S[I]) xor Byte(Key[J]));
975     inc(I);
976     dec(J);
977     end;
978     end;
979    
980     function SubstituteS(const S, FromStr, ToStr : ShortString) : ShortString;
981     {-Map the characters found in FromStr to the corresponding ones in ToStr.}
982     var
983     P : Cardinal;
984     I : Byte;
985     begin
986     Result := S;
987     if Length(FromStr) = Length(ToStr) then
988     for I := 1 to Length(Result) do begin
989     if StrChPosS(FromStr, S[I], P) then
990     Result[I] := ToStr[P];
991     end;
992     end;
993    
994     function FilterS(const S, Filters : ShortString) : ShortString;
995     {-Remove characters from a string. The characters to remove are specified in
996     ChSet.}
997     var
998     I : Cardinal;
999     Len : Cardinal;
1000     begin
1001     Len := 0;
1002     for I := 1 to Length(S) do
1003     if not CharExistsS(Filters, S[I]) then begin
1004     Inc(Len);
1005     Result[Len] := S[I];
1006     end;
1007     Result[0] := AnsiChar(Len);
1008     end;
1009    
1010     {--------------- Word / Char manipulation -------------------------}
1011    
1012     function CharExistsS(const S : String; C : Char) : Boolean; overload;
1013     var
1014     I: Integer;
1015     begin
1016     Result := False;
1017     for I := 1 to Length(S) do
1018     begin
1019     if S[I] = C then
1020     begin
1021     Result := True;
1022     Break;
1023     end;
1024     end;
1025     end;
1026    
1027     function CharExistsS(const S : ShortString; C : AnsiChar) : Boolean; overload;
1028     {-Determine whether a given character exists in a string. }
1029     register;
1030     asm
1031     xor ecx, ecx
1032     mov ch, [eax]
1033     inc eax
1034     or ch, ch
1035     jz @@Done
1036     jmp @@5
1037    
1038     @@Loop:
1039     cmp dl, [eax+3]
1040     jne @@1
1041     inc cl
1042     jmp @@Done
1043    
1044     @@1:
1045     cmp dl, [eax+2]
1046     jne @@2
1047     inc cl
1048     jmp @@Done
1049    
1050     @@2:
1051     cmp dl, [eax+1]
1052     jne @@3
1053     inc cl
1054     jmp @@Done
1055    
1056     @@3:
1057     cmp dl, [eax+0]
1058     jne @@4
1059     inc cl
1060     jmp @@Done
1061    
1062     @@4:
1063     add eax, 4
1064     sub ch, 4
1065     jna @@Done
1066    
1067     @@5:
1068     cmp ch, 4
1069     jae @@Loop
1070    
1071     cmp ch, 3
1072     je @@1
1073    
1074     cmp ch, 2
1075     je @@2
1076    
1077     cmp ch, 1
1078     je @@3
1079    
1080     @@Done:
1081     xor eax, eax
1082     mov al, cl
1083     end;
1084    
1085     function CharCountS(const S : ShortString; C : AnsiChar) : Byte;
1086     {-Count the number of a given character in a string. }
1087     register;
1088     asm
1089     xor ecx, ecx
1090     mov ch, [eax]
1091     inc eax
1092     or ch, ch
1093     jz @@Done
1094     jmp @@5
1095    
1096     @@Loop:
1097     cmp dl, [eax+3]
1098     jne @@1
1099     inc cl
1100    
1101     @@1:
1102     cmp dl, [eax+2]
1103     jne @@2
1104     inc cl
1105    
1106     @@2:
1107     cmp dl, [eax+1]
1108     jne @@3
1109     inc cl
1110    
1111     @@3:
1112     cmp dl, [eax+0]
1113     jne @@4
1114     inc cl
1115    
1116     @@4:
1117     add eax, 4
1118     sub ch, 4
1119     jna @@Done
1120    
1121     @@5:
1122     cmp ch, 4
1123     jae @@Loop
1124    
1125     cmp ch, 3
1126     je @@1
1127    
1128     cmp ch, 2
1129     je @@2
1130    
1131     cmp ch, 1
1132     je @@3
1133    
1134     @@Done:
1135     mov al, cl
1136     end;
1137    
1138     function WordCountS(const S, WordDelims : ShortString) : Cardinal;
1139     {-Given an array of word delimiters, return the number of words in a string.}
1140     var
1141     I : Integer;
1142     SLen : Byte;
1143     begin
1144     Result := 0;
1145     I := 1;
1146     SLen := Length(S);
1147    
1148     while I <= SLen do begin
1149     {skip over delimiters}
1150     while (I <= SLen) and CharExistsS(WordDelims, S[I]) do
1151     Inc(I);
1152    
1153     {if we're not beyond end of S, we're at the start of a word}
1154     if I <= SLen then
1155     Inc(Result);
1156    
1157     {find the end of the current word}
1158     while (I <= SLen) and not CharExistsS(WordDelims, S[I]) do
1159     Inc(I);
1160     end;
1161     end;
1162    
1163     function WordPositionS(N : Cardinal; const S, WordDelims : ShortString;
1164     var Pos : Cardinal) : Boolean;
1165     {-Given an array of word delimiters, set Pos to the start position of the
1166     N'th word in a string. Result indicates success/failure.}
1167     var
1168     I : Cardinal;
1169     Count : Byte;
1170     SLen : Byte absolute S;
1171     begin
1172     Count := 0;
1173     I := 1;
1174     Result := False;
1175    
1176     while (I <= SLen) and (Count <> N) do begin
1177     {skip over delimiters}
1178     while (I <= SLen) and CharExistsS(WordDelims, S[I]) do
1179     Inc(I);
1180    
1181     {if we're not beyond end of S, we're at the start of a word}
1182     if I <= SLen then
1183     Inc(Count);
1184    
1185     {if not finished, find the end of the current word}
1186     if Count <> N then
1187     while (I <= SLen) and not CharExistsS(WordDelims, S[I]) do
1188     Inc(I)
1189     else begin
1190     Pos := I;
1191     Result := True;
1192     end;
1193     end;
1194     end;
1195    
1196     function ExtractWordS(N : Cardinal; const S, WordDelims : ShortString) : ShortString;
1197     {-Given an array of word delimiters, return the N'th word in a string.}
1198     var
1199     I : Cardinal;
1200     Len : Byte;
1201     SLen : Byte absolute S;
1202     begin
1203     Len := 0;
1204     if WordPositionS(N, S, WordDelims, I) then
1205     {find the end of the current word}
1206     while (I <= SLen) and not CharExistsS(WordDelims, S[I]) do begin
1207     {add the I'th character to result}
1208     Inc(Len);
1209     Result[Len] := S[I];
1210     Inc(I);
1211     end;
1212     Result[0] := AnsiChar(Len);
1213     end;
1214    
1215     function AsciiCountS(const S, WordDelims : ShortString; Quote : AnsiChar) : Cardinal;
1216     {-Return the number of words in a string.}
1217     var
1218     I : Cardinal;
1219     InQuote : Boolean;
1220     SLen : Byte absolute S;
1221     begin
1222     Result := 0;
1223     I := 1;
1224     InQuote := False;
1225     while I <= SLen do begin
1226     {skip over delimiters}
1227     while (I <= SLen) and (S[i] <> Quote) and CharExistsS(WordDelims, S[I]) do
1228     Inc(I);
1229     {if we're not beyond end of S, we're at the start of a word}
1230     if I <= SLen then
1231     Inc(Result);
1232     {find the end of the current word}
1233     while (I <= SLen) and (InQuote or not CharExistsS(WordDelims, S[I])) do begin
1234     if S[I] = Quote then
1235     InQuote := not InQuote;
1236     Inc(I);
1237     end;
1238     end;
1239     end;
1240    
1241     function AsciiPositionS(N : Cardinal; const S, WordDelims : ShortString;
1242     Quote : AnsiChar; var Pos : Cardinal) : Boolean;
1243     {-Return the position of the N'th word in a string.}
1244     var
1245     I : Cardinal;
1246     Count : Byte;
1247     InQuote : Boolean;
1248     SLen : Byte absolute S;
1249     begin
1250     Count := 0;
1251     InQuote := False;
1252     Result := False;
1253     I := 1;
1254     while (I <= SLen) and (Count <> N) do begin
1255     {skip over delimiters}
1256     while (I <= SLen) and (S[I] <> Quote) and CharExistsS(WordDelims, S[I]) do
1257     Inc(I);
1258     {if we're not beyond end of S, we're at the start of a word}
1259     if I <= SLen then
1260     Inc(Count);
1261     {if not finished, find the end of the current word}
1262     if Count <> N then
1263     while (I <= SLen) and (InQuote or not CharExistsS(WordDelims, S[I])) do begin
1264     if S[I] = Quote then
1265     InQuote := not InQuote;
1266     Inc(I);
1267     end
1268     else begin
1269     Pos := I;
1270     Result := True;
1271     end;
1272     end;
1273     end;
1274    
1275     function ExtractAsciiS(N : Cardinal; const S, WordDelims : ShortString;
1276     Quote : AnsiChar) : ShortString;
1277     {-Given an array of word delimiters, return the N'th word in a string. Any
1278     text within Quote characters is counted as one word.}
1279     var
1280     I : Cardinal;
1281     Len : Byte;
1282     SLen : Byte absolute S;
1283     InQuote : Boolean;
1284     begin
1285     Len := 0;
1286     InQuote := False;
1287     if AsciiPositionS(N, S, WordDelims, Quote, I) then
1288     {find the end of the current word}
1289     while (I <= SLen) and ((InQuote) or not CharExistsS(WordDelims, S[I])) do begin
1290     {add the I'th character to result}
1291     Inc(Len);
1292     if S[I] = Quote then
1293     InQuote := not(InQuote);
1294     Result [Len] := S[I];
1295     Inc(I);
1296     end;
1297     Result [0] := AnsiChar(Len);
1298     end;
1299    
1300     procedure WordWrapS(const InSt : ShortString; var OutSt, Overlap : ShortString;
1301     Margin : Cardinal; PadToMargin : Boolean);
1302     {-Wrap a text string at a specified margin.}
1303     var
1304     EOS, BOS : Cardinal;
1305     InStLen : Byte;
1306     OutStLen : Byte absolute OutSt;
1307     OvrLen : Byte absolute Overlap;
1308     begin
1309     InStLen := Length(InSt);
1310    
1311     {!!.02 - Added }
1312     { handle empty string on input }
1313     if InStLen = 0 then begin
1314     OutSt := '';
1315     Overlap := '';
1316     Exit;
1317     end;
1318     {!!.02 - End Added }
1319    
1320     {find the end of the output string}
1321     if InStLen > Margin then begin
1322     {find the end of the word at the margin, if any}
1323     EOS := Margin;
1324     while (EOS <= InStLen) and (InSt[EOS] <> ' ') do
1325     Inc(EOS);
1326     if EOS > InStLen then
1327     EOS := InStLen;
1328    
1329     {trim trailing blanks}
1330     while (InSt[EOS] = ' ') and (EOS > 0) do
1331     Dec(EOS);
1332    
1333     if EOS > Margin then begin
1334     {look for the space before the current word}
1335     while (EOS > 0) and (InSt[EOS] <> ' ') do
1336     Dec(EOS);
1337    
1338     {if EOS = 0 then we can't wrap it}
1339     if EOS = 0 then
1340     EOS := Margin
1341     else
1342     {trim trailing blanks}
1343     while (InSt[EOS] = ' ') and (EOS > 0) do
1344     Dec(EOS);
1345     end;
1346     end else
1347     EOS := InStLen;
1348    
1349     {copy the unwrapped portion of the line}
1350     OutStLen := EOS;
1351     Move(InSt[1], OutSt[1], OutStLen);
1352    
1353     {find the start of the next word in the line}
1354     BOS := EOS+1;
1355     while (BOS <= InStLen) and (InSt[BOS] = ' ') do
1356     Inc(BOS);
1357    
1358     if BOS > InStLen then
1359     OvrLen := 0
1360     else begin
1361     {copy from the start of the next word to the end of the line}
1362     OvrLen := Succ(InStLen-BOS);
1363     Move(InSt[BOS], Overlap[1], OvrLen);
1364     end;
1365    
1366     {pad the end of the output string if requested}
1367     if PadToMargin and (OutStLen < Margin) then begin
1368     FillChar(OutSt[OutStLen+1], Margin-OutStLen, ' ');
1369     OutStLen := Margin;
1370     end;
1371     end;
1372    
1373     {--------------- String comparison and searching -----------------}
1374     function CompStringS(const S1, S2 : ShortString) : Integer;
1375     {-Compare two strings.}
1376     register;
1377     asm
1378     push edi
1379     mov edi, edx { EDI points to S2 }
1380     push esi
1381     mov esi, eax { ESI points to S1 }
1382    
1383     xor ecx, ecx
1384    
1385     mov dl, [edi] { DL = Length(S2) }
1386     inc edi { EDI points to S2[1] }
1387     mov cl, [esi]
1388     inc esi { CL = Length(S1) - ESI points to S1[1] }
1389    
1390     or eax, -1 { EAX holds temporary result }
1391    
1392     cmp cl, dl { Compare lengths }
1393     je @@EqLen { Lengths equal? }
1394     jb @@Comp { Jump if S1 shorter than S1 }
1395    
1396     inc eax { S1 longer than S2 }
1397     mov cl, dl { Length(S2) in CL }
1398    
1399     @@EqLen:
1400     inc eax { Equal or greater }
1401    
1402     @@Comp:
1403     or ecx, ecx
1404     jz @@Done { Done if either is empty }
1405    
1406     repe cmpsb { Compare until no match or ECX = 0 }
1407     je @@Done { If Equal, result ready based on length }
1408    
1409     mov eax, 1
1410     ja @@Done { S1 Greater? Return 1 }
1411     or eax, -1 { Else S1 Less, Return -1 }
1412    
1413     @@Done:
1414     pop esi
1415     pop edi
1416     end;
1417    
1418     function CompUCStringS(const S1, S2 : ShortString) : Integer;
1419     {-Compare two strings. This compare is not case sensitive.}
1420     register;
1421     asm
1422     push ebx
1423     push edi { Save registers }
1424     push esi
1425    
1426     mov edi, edx { EDI points to S2 }
1427     mov esi, eax { ESI points to S1 }
1428    
1429     xor eax, eax { EAX holds chars from S1 }
1430     xor ecx, ecx { ECX holds count of chars to compare }
1431     xor edx, edx { DH holds temp result, DL chars from S2 }
1432     or ebx, -1
1433    
1434     mov al, [edi] { AH = Length(S2) }
1435     inc edi { EDI points to S2[1] }
1436     mov cl, [esi] { CL = Length(S1) - SI points to S1[1] }
1437     inc esi
1438    
1439     cmp cl, al { Compare lengths }
1440     je @@EqLen { Lengths equal? }
1441     jb @@Comp { Jump if S1 shorter than S1 }
1442    
1443     inc ebx { S1 longer than S2 }
1444     mov cl, al { Shorter length in CL }
1445    
1446     @@EqLen:
1447     inc ebx { Equal or greater }
1448    
1449     @@Comp:
1450     or ecx, ecx
1451     jz @@Done { Done if lesser string is empty }
1452    
1453     @@Start:
1454     mov al, [esi] { S1[?] into AL }
1455     inc esi
1456    
1457     push ecx { Save registers }
1458     push edx
1459     push eax { Push Char onto stack for CharUpper }
1460     call CharUpper
1461     pop edx { Restore registers }
1462     pop ecx
1463    
1464     mov dl, [edi] { S2[?] into DL }
1465     inc edi { Point EDI to next char in S2 }
1466     mov dh, al
1467     mov al, dl
1468     mov dl, dh
1469    
1470     push ecx { Save registers }
1471     push edx
1472     push eax { Push Char onto stack for CharUpper }
1473     call CharUpper
1474     pop edx { Restore registers }
1475     pop ecx
1476    
1477     cmp dl, al { Compare until no match }
1478     jnz @@Output
1479     dec ecx
1480     jnz @@Start
1481    
1482     je @@Done { If Equal, result ready based on length }
1483    
1484     @@Output:
1485     mov ebx, 1
1486     ja @@Done { S1 Greater? Return 1 }
1487     or ebx, -1 { Else S1 Less, Return -1 }
1488    
1489     @@Done:
1490     mov eax, ebx { Result into AX }
1491     pop esi { Restore Registers }
1492     pop edi
1493     pop ebx
1494     end;
1495    
1496     function SoundexS(const S : ShortString) : ShortString; assembler;
1497     {-Return 4 character soundex of an input string}
1498     register;
1499     const
1500     SoundexTable : array[0..255] of Char =
1501     (#0, #0, #0, #0, #0, #0, #0, #0, #0, #0,
1502     #0, #0, #0, #0, #0, #0, #0, #0, #0, #0,
1503     #0, #0, #0, #0, #0, #0, #0, #0, #0, #0,
1504     #0, #0, #0, #0, #0, #0, #0, #0, #0, #0,
1505     #0, #0, #0, #0, #0, #0, #0, #0, #0, #0,
1506     #0, #0, #0, #0, #0, #0, #0, #0, #0, #0,
1507     #0, #0, #0, #0, #0,
1508     { A B C D E F G H I J K L M }
1509     #0, '1', '2', '3', #0, '1', '2', #0, #0, '2', '2', '4', '5',
1510     { N O P Q R S T U V W X Y X }
1511     '5', #0, '1', '2', '6', '2', '3', #0, '1', #0, '2', #0, '2',
1512     #0, #0, #0, #0, #0, #0,
1513     { a b c d e f g h i j k l m }
1514     #0, '1', '2', '3', #0, '1', '2', #0, #0, '2', '2', '4', '5',
1515     { n o p q r s t u v w x y x }
1516     '5', #0, '1', '2', '6', '2', '3', #0, '1', #0, '2', #0, '2',
1517     #0, #0, #0, #0, #0, #0, #0, #0, #0, #0,
1518     #0, #0, #0, #0, #0, #0, #0, #0, #0, #0,
1519     #0, #0, #0, #0, #0, #0, #0, #0, #0, #0,
1520     #0, #0, #0, #0, #0, #0, #0, #0, #0, #0,
1521     #0, #0, #0, #0, #0, #0, #0, #0, #0, #0,
1522     #0, #0, #0, #0, #0, #0, #0, #0, #0, #0,
1523     #0, #0, #0, #0, #0, #0, #0, #0, #0, #0,
1524     #0, #0, #0, #0, #0, #0, #0, #0, #0, #0,
1525     #0, #0, #0, #0, #0, #0, #0, #0, #0, #0,
1526     #0, #0, #0, #0, #0, #0, #0, #0, #0, #0,
1527     #0, #0, #0, #0, #0, #0, #0, #0, #0, #0,
1528     #0, #0, #0, #0, #0, #0, #0, #0, #0, #0,
1529     #0, #0, #0, #0, #0, #0, #0, #0, #0, #0,
1530     #0, #0, #0);
1531     asm
1532     push edi
1533     mov edi, edx { EDI => output string }
1534     push ebx
1535     push esi
1536    
1537     mov esi, eax { ESI => input string }
1538     mov byte ptr [edi], 4 { Prepare output string to be #4'0000' }
1539     mov dword ptr [edi+1], '0000'
1540     inc edi
1541    
1542     mov cl, byte ptr [esi]
1543     inc esi
1544     or cl, cl { Exit if null string }
1545     jz @@Done
1546    
1547     xor eax, eax
1548     mov al, [esi] { Get first character of input string }
1549     inc esi
1550    
1551     push ecx { Save ECX across call to CharUpper }
1552     push eax { Push Char onto stack for CharUpper }
1553     call CharUpper { Uppercase AL }
1554     pop ecx { Restore saved register }
1555    
1556     mov [edi], al { Store first output character }
1557     inc edi
1558    
1559     dec cl { One input character used }
1560     jz @@Done { Was input string one char long? }
1561    
1562     mov ch, 03h { Output max 3 chars beyond first }
1563     mov edx, offset SoundexTable { EDX => Soundex table }
1564     xor eax, eax { Prepare for address calc }
1565     xor bl, bl { BL will be used to store 'previous char' }
1566    
1567     @@Next:
1568     mov al, [esi] { Get next char in AL }
1569     inc esi
1570     mov al, [edx+eax] { Get soundex code into AL }
1571     or al, al { Is AL zero? }
1572     jz @@NoStore { If yes, skip this char }
1573     cmp bl, al { Is it the same as the previous stored char? }
1574     je @@NoStore { If yes, skip this char }
1575     mov [edi], al { Store char to Dest }
1576     inc edi
1577     dec ch { Decrement output counter }
1578     jz @@Done { If zero, we're done }
1579     mov bl, al { New previous character }
1580    
1581     @@NoStore:
1582     dec cl { Decrement input counter }
1583     jnz @@Next
1584    
1585     @@Done:
1586     pop esi
1587     pop ebx
1588     pop edi
1589     end;
1590    
1591     function MakeLetterSetS(const S : ShortString) : Longint;
1592     {-Return a bit-mapped long storing the individual letters contained in S.}
1593     register;
1594     asm
1595     push ebx { Save registers }
1596     push esi
1597    
1598     mov esi, eax { ESI => string }
1599     xor ecx, ecx { Zero ECX }
1600     xor edx, edx { Zero EDX }
1601     xor eax, eax { Zero EAX }
1602     add cl, [esi] { CX = Length(S) }
1603     jz @@Exit { Done if ECX is 0 }
1604     inc esi
1605    
1606     @@Next:
1607     mov al, [esi] { EAX has next char in S }
1608     inc esi
1609    
1610     push ecx { Save registers }
1611     push edx
1612     push eax { Push Char onto stack for CharUpper }
1613     call CharUpper
1614     pop edx { Restore registers }
1615     pop ecx
1616    
1617     sub eax, 'A' { Convert to bit number }
1618     cmp eax, 'Z'-'A' { Was char in range 'A'..'Z'? }
1619     ja @@Skip { Skip it if not }
1620    
1621     mov ebx, eax { Exchange EAX and ECX }
1622     mov eax, ecx
1623     mov ecx, ebx
1624     ror edx, cl
1625     or edx, 01h { Set appropriate bit }
1626     rol edx, cl
1627     mov ebx, eax { Exchange EAX and ECX }
1628     mov eax, ecx
1629     mov ecx, ebx
1630    
1631     @@Skip:
1632     dec ecx
1633     jnz @@Next { Get next character }
1634    
1635     @@Exit:
1636     mov eax, edx { Move EDX to result }
1637     pop esi { Restore registers }
1638     pop ebx
1639     end;
1640    
1641     procedure BMMakeTableS(const MatchString : ShortString; var BT : BTable);
1642     {-Build a Boyer-Moore link table}
1643     register;
1644     asm
1645     push edi { Save registers because they will be changed }
1646     push esi
1647     mov esi, eax { Move EAX to ESI }
1648     push ebx
1649    
1650     xor eax, eax { Zero EAX }
1651     xor ecx, ecx { Zero ECX }
1652     mov cl, [esi] { ECX has length of MatchString }
1653     inc esi
1654    
1655     mov ch, cl { Duplicate CL in CH }
1656     mov eax, ecx { Fill each byte in EAX with length }
1657     shl eax, 16
1658     or eax, ecx
1659     mov edi, edx { Point to the table }
1660     mov ecx, 64 { Fill table bytes with length }
1661     rep stosd
1662     cmp al, 1 { If length <= 1, we're done }
1663     jbe @@MTDone
1664     xor ebx, ebx { Zero EBX }
1665     mov cl, al { Restore CL to length of string }
1666     dec ecx
1667    
1668     @@MTNext:
1669     mov al, [esi] { Load table with positions of letters }
1670     mov bl, al { that exist in the search string }
1671     inc esi
1672     mov [edx+ebx], cl
1673     dec cl
1674     jnz @@MTNext
1675    
1676     @@MTDone:
1677     pop ebx { Restore registers }
1678     pop esi
1679     pop edi
1680     end;
1681    
1682     function BMSearchS(var Buffer; BufLength : Cardinal; var BT : BTable;
1683     const MatchString : ShortString ; var Pos : Cardinal) : Boolean; assembler;
1684     {-Use the Boyer-Moore search method to search a buffer for a string.}
1685     register;
1686     var
1687     BufPtr : Pointer;
1688     asm
1689     push edi { Save registers since we will be changing }
1690     push esi
1691     push ebx
1692    
1693     mov BufPtr, eax { Copy Buffer to local variable and EDI }
1694     mov edi, eax
1695     mov ebx, ecx { Copy BT ptr to EBX }
1696     mov ecx, edx { Length of buffer to ECX }
1697     mov esi, MatchString { Set ESI to beginning of MatchString }
1698     xor eax, eax { Zero EAX }
1699    
1700     mov dl, [esi] { Length of MatchString in EDX }
1701     inc esi
1702     and edx, 0FFh
1703    
1704     cmp dl, 1 { Check to see if we have a trivial case }
1705     ja @@BMSInit { If Length(MatchString) > 1 do BM search }
1706     jb @@BMSNotFound { If Length(MatchString) = 0 we're done }
1707    
1708     mov al,[esi] { If Length(MatchString) = 1 do a REPNE SCASB }
1709     mov ebx, edi
1710     repne scasb
1711     jne @@BMSNotFound { No match during REP SCASB }
1712     mov esi, Pos { Set position in Pos }
1713     {dec edi} { Found, calculate position }
1714     sub edi, ebx
1715     mov eax, 1 { Set result to True }
1716     mov [esi], edi
1717     jmp @@BMSDone { We're done }
1718    
1719     @@BMSInit:
1720     dec edx { Set up for BM Search }
1721     add esi, edx { Set ESI to end of MatchString }
1722     add ecx, edi { Set ECX to end of buffer }
1723     add edi, edx { Set EDI to first check point }
1724     std { Backward string ops }
1725     mov dh, [esi] { Set DH to character we'll be looking for }
1726     dec esi { Dec ESI in prep for BMSFound loop }
1727     jmp @@BMSComp { Jump to first comparison }
1728    
1729     @@BMSNext:
1730     mov al, [ebx+eax] { Look up skip distance from table }
1731     add edi, eax { Skip EDI ahead to next check point }
1732    
1733     @@BMSComp:
1734     cmp edi, ecx { Have we reached end of buffer? }
1735     jae @@BMSNotFound { If so, we're done }
1736     mov al, [edi] { Move character from buffer into AL for comparison }
1737     cmp dh, al { Compare }
1738     jne @@BMSNext { If not equal, go to next checkpoint }
1739    
1740     push ecx { Save ECX }
1741     dec edi
1742     xor ecx, ecx { Zero ECX }
1743     mov cl, dl { Move Length(MatchString) to ECX }
1744     repe cmpsb { Compare MatchString to buffer }
1745     je @@BMSFound { If equal, string is found }
1746    
1747     mov al, dl { Move Length(MatchString) to AL }
1748     sub al, cl { Calculate offset that string didn't match }
1749     add esi, eax { Move ESI back to end of MatchString }
1750     add edi, eax { Move EDI to pre-string compare location }
1751     inc edi
1752     mov al, dh { Move character back to AL }
1753     pop ecx { Restore ECX }
1754     jmp @@BMSNext { Do another compare }
1755    
1756     @@BMSFound: { EDI points to start of match }
1757     mov edx, BufPtr { Move pointer to buffer into EDX }
1758     mov esi, Pos
1759     sub edi, edx { Calculate position of match }
1760     mov eax, edi
1761     inc eax
1762     inc eax { Pos is one based }
1763     mov [esi], eax { Set Pos to position of match }
1764     mov eax, 1 { Set result to True }
1765     pop ecx { Restore ESP }
1766     jmp @@BMSDone
1767    
1768     @@BMSNotFound:
1769     xor eax, eax { Set result to False }
1770    
1771     @@BMSDone:
1772     cld { Restore direction flag }
1773     pop ebx { Restore registers }
1774     pop esi
1775     pop edi
1776     end;
1777    
1778     function BMSearchUCS(var Buffer; BufLength : Cardinal; var BT : BTable;
1779     const MatchString : ShortString ; var Pos : Cardinal) : Boolean; assembler;
1780     {-Use the Boyer-Moore search method to search a buffer for a string. This
1781     search is not case sensitive.}
1782     register;
1783     var
1784     BufPtr : Pointer;
1785     asm
1786     push edi { Save registers since we will be changing }
1787     push esi
1788     push ebx
1789    
1790     mov BufPtr, eax { Copy Buffer to local variable and ESI }
1791     mov edi, eax
1792     mov ebx, ecx { Copy BT ptr to EBX }
1793     mov ecx, edx { Length of buffer to ECX }
1794     mov esi, MatchString { Set ESI to beginning of MatchString }
1795     xor eax, eax { Zero EAX }
1796    
1797     mov dl, byte ptr [esi] { Length of MatchString in EDX }
1798     and edx, 0FFh { Clean up EDX }
1799     inc esi { Set ESI to first character }
1800    
1801     or dl, dl { Check to see if we have a trivial case }
1802     jz @@BMSNotFound { If Length(MatchString) = 0 we're done }
1803    
1804     @@BMSInit:
1805     dec edx { Set up for BM Search }
1806     add esi, edx { Set ESI to end of MatchString }
1807     add ecx, edi { Set ECX to end of buffer }
1808     add edi, edx { Set EDI to first check point }
1809     std { Backward string ops }
1810     mov dh, [esi] { Set DH to character we'll be looking for }
1811     dec esi { Dec ESI in prep for BMSFound loop }
1812     jmp @@BMSComp { Jump to first comparison }
1813    
1814     @@BMSNext:
1815     mov al, [ebx+eax] { Look up skip distance from table }
1816     add edi, eax { Skip EDI ahead to next check point }
1817    
1818     @@BMSComp:
1819     cmp edi, ecx { Have we reached end of buffer? }
1820     jae @@BMSNotFound { If so, we're done }
1821    
1822     push ebx { Save registers }
1823     push ecx
1824     push edx
1825     mov al, [edi] { Move character from buffer into AL for comparison }
1826     push eax { Push Char onto stack for CharUpper }
1827     cld
1828     call CharUpper
1829     std
1830     pop edx { Restore registers }
1831     pop ecx
1832     pop ebx
1833    
1834     cmp dh, al { Compare }
1835     jne @@BMSNext { If not equal, go to next checkpoint }
1836    
1837     push ecx { Save ECX }
1838     dec edi
1839     xor ecx, ecx { Zero ECX }
1840     mov cl, dl { Move Length(MatchString) to ECX }
1841     jecxz @@BMSFound { If ECX is zero, string is found }
1842    
1843     @@StringComp:
1844     xor eax, eax
1845     mov al, [edi] { Get char from buffer }
1846     dec edi { Dec buffer index }
1847    
1848     push ebx { Save registers }
1849     push ecx
1850     push edx
1851     push eax { Push Char onto stack for CharUpper }
1852     cld
1853     call CharUpper
1854     std
1855     pop edx { Restore registers }
1856     pop ecx
1857     pop ebx
1858    
1859     mov ah, al { Move buffer char to AH }
1860     mov al, [esi] { Get MatchString char }
1861     dec esi
1862     cmp ah, al { Compare }
1863     loope @@StringComp { OK? Get next character }
1864     je @@BMSFound { Matched! }
1865    
1866     xor ah, ah { Zero AH }
1867     mov al, dl { Move Length(MatchString) to AL }
1868     sub al, cl { Calculate offset that string didn't match }
1869     add esi, eax { Move ESI back to end of MatchString }
1870     add edi, eax { Move EDI to pre-string compare location }
1871     inc edi
1872     mov al, dh { Move character back to AL }
1873     pop ecx { Restore ECX }
1874     jmp @@BMSNext { Do another compare }
1875    
1876     @@BMSFound: { EDI points to start of match }
1877     mov edx, BufPtr { Move pointer to buffer into EDX }
1878     mov esi, Pos
1879     sub edi, edx { Calculate position of match }
1880     mov eax, edi
1881     inc eax
1882     inc eax { Pos is one based }
1883     mov [esi], eax { Set Pos to position of match }
1884     mov eax, 1 { Set result to True }
1885     pop ecx { Restore ESP }
1886     jmp @@BMSDone
1887    
1888     @@BMSNotFound:
1889     xor eax, eax { Set result to False }
1890    
1891     @@BMSDone:
1892     cld { Restore direction flag }
1893     pop ebx { Restore registers }
1894     pop esi
1895     pop edi
1896     end;
1897    
1898     {--------------- DOS pathname parsing -----------------}
1899    
1900     function DefaultExtensionS(const Name, Ext : ShortString) : ShortString;
1901     {-Return a file name with a default extension attached.}
1902     var
1903     DotPos : Cardinal;
1904     begin
1905     if HasExtensionS(Name, DotPos) then
1906     Result := Name
1907     else if Name = '' then
1908     Result := ''
1909     else
1910     Result := Name + '.' + Ext;
1911     end;
1912    
1913     function ForceExtensionS(const Name, Ext : ShortString) : ShortString;
1914     {-Force the specified extension onto the file name.}
1915     var
1916     DotPos : Cardinal;
1917     begin
1918     if HasExtensionS(Name, DotPos) then
1919     Result := Copy(Name, 1, DotPos) + Ext
1920     else if Name = '' then
1921     Result := ''
1922     else
1923     Result := Name + '.' + Ext;
1924     end;
1925    
1926     function JustFilenameS(const PathName : ShortString) : ShortString;
1927     {-Return just the filename and extension of a pathname.}
1928     var
1929     I : Longint;
1930     begin
1931     Result := '';
1932     if PathName = '' then
1933     Exit;
1934     I := Succ(Length(PathName));
1935     repeat
1936     Dec(I);
1937     until (I = 0) or (PathName[I] in DosDelimSet); {!!.01}
1938     Result := Copy(PathName, Succ(I), StMaxFileLen);
1939     end;
1940    
1941     function JustNameS(const PathName : ShortString) : ShortString;
1942     {-Return just the filename (no extension, path, or drive) of a pathname.}
1943     var
1944     DotPos : Cardinal;
1945     begin
1946     Result := JustFileNameS(PathName);
1947     if HasExtensionS(Result, DotPos) then
1948     Result := Copy(Result, 1, DotPos-1);
1949     end;
1950    
1951     function JustExtensionS(const Name : ShortString) : ShortString;
1952     {-Return just the extension of a pathname.}
1953     var
1954     DotPos : Cardinal;
1955     begin
1956     if HasExtensionS(Name, DotPos) then
1957     Result := Copy(Name, Succ(DotPos), StMaxFileLen)
1958     else
1959     Result := '';
1960     end;
1961    
1962     function JustPathnameS(const PathName : ShortString) : ShortString;
1963     {-Return just the drive and directory portion of a pathname.}
1964     var
1965     I : Longint;
1966     begin
1967     I := Succ(Length(PathName));
1968     repeat
1969     Dec(I);
1970     until (I = 0) or (PathName[I] in DosDelimSet); {!!.01}
1971    
1972     if I = 0 then
1973     {Had no drive or directory name}
1974     Result [0] := #0
1975     else if I = 1 then
1976     {Either the root directory of default drive or invalid pathname}
1977     Result := PathName[1]
1978     else if (PathName[I] = '\') then begin
1979     if PathName[Pred(I)] = ':' then
1980     {Root directory of a drive, leave trailing backslash}
1981     Result := Copy(PathName, 1, I)
1982     else
1983     {Subdirectory, remove the trailing backslash}
1984     Result := Copy(PathName, 1, Pred(I));
1985     end else
1986     {Either the default directory of a drive or invalid pathname}
1987     Result := Copy(PathName, 1, I);
1988     end;
1989    
1990     function AddBackSlashS(const DirName : ShortString) : ShortString;
1991     {-Add a default backslash to a directory name}
1992     begin
1993     Result := DirName;
1994     if (Length(Result) = 0) then
1995     Exit;
1996     if ((Length(Result) = 2) and (Result[2] = ':')) or
1997     ((Length(Result) > 2) and (Result[Length(Result)] <> '\')) then
1998     Result := Result + '\';
1999     end;
2000    
2001     function CleanFileNameS(const FileName : ShortString) : ShortString;
2002     {-Return filename with at most 8 chars of name and 3 of extension}
2003     var
2004     DotPos : Cardinal;
2005     NameLen : Cardinal;
2006     begin
2007     if HasExtensionS(FileName, DotPos) then begin
2008     {Take the first 8 chars of name and first 3 chars of extension}
2009     NameLen := Pred(DotPos);
2010     if NameLen > 8 then
2011     NameLen := 8;
2012     Result := Copy(FileName, 1, NameLen)+Copy(FileName, DotPos, 4);
2013     end else
2014     {Take the first 8 chars of name}
2015     Result := Copy(FileName, 1, 8);
2016     end;
2017    
2018     function CleanPathNameS(const PathName : ShortString) : ShortString;
2019     {-Return a pathname cleaned up as DOS does it.}
2020     var
2021     I : Longint;
2022     S : ShortString;
2023     begin
2024     Result[0] := #0;
2025     S := PathName;
2026    
2027     I := Succ(Length(S));
2028     repeat
2029     dec(I);
2030     if I > 2 then
2031     if (S[I] = '\') and (S[I-1] = '\') then
2032     if (S[I-2] <> ':') then
2033     Delete(S, I, 1);
2034     until I <= 0;
2035    
2036     I := Succ(Length(S));
2037     repeat
2038     {Get the next directory or drive portion of pathname}
2039     repeat
2040     Dec(I);
2041     until (I = 0) or (S[I] in DosDelimSet); {!!.02}
2042    
2043     {Clean it up and prepend it to output string}
2044     Result := CleanFileNameS(Copy(S, Succ(I), StMaxFileLen)) + Result;
2045     if I > 0 then begin
2046     Result := S[I] + Result;
2047     Delete(S, I, 255);
2048     end;
2049     until I <= 0;
2050    
2051     end;
2052    
2053     function HasExtensionS(const Name : ShortString; var DotPos : Cardinal) : Boolean;
2054     {-Determine if a pathname contains an extension and, if so, return the
2055     position of the dot in front of the extension.}
2056     var
2057     I : Cardinal;
2058     begin
2059     DotPos := 0;
2060     for I := Length(Name) downto 1 do
2061     if (Name[I] = '.') and (DotPos = 0) then
2062     DotPos := I;
2063     Result := (DotPos > 0)
2064     {and (Pos('\', Copy(Name, Succ(DotPos), MaxFileLen)) = 0);}
2065     and not CharExistsS(Copy(Name, Succ(DotPos), StMaxFileLen), '\');
2066     end;
2067    
2068     {------------------ Formatting routines --------------------}
2069    
2070    
2071     function CommaizeChS(L : Longint; Ch : AnsiChar) : ShortString;
2072     {-Convert a long integer to a string with Ch in comma positions}
2073     var
2074     NumCommas, I, Len : Cardinal;
2075     Neg : Boolean;
2076     begin
2077     if L < 0 then begin
2078     Neg := True;
2079     L := Abs(L);
2080     end else
2081     Neg := False;
2082     Result := Long2StrS(L);
2083     Len := Length(Result);
2084     NumCommas := (Len - 1) div 3;
2085     for I := 1 to NumCommas do
2086     System.Insert(Ch, Result, Len-(I * 3)+1);
2087     if Neg then
2088     System.Insert('-', Result, 1);
2089     end;
2090    
2091     function CommaizeS(L : LongInt) : ShortString;
2092     {-Convert a long integer to a string with commas}
2093     begin
2094     Result := CommaizeChS(L, ',');
2095     end;
2096    
2097     function FormPrimS(const Mask : ShortString; R : TstFloat; const LtCurr,
2098     RtCurr : ShortString; Sep, DecPt : AnsiChar;
2099     AssumeDP : Boolean) : ShortString;
2100     {-Returns a formatted string with digits from R merged into the Mask}
2101     const
2102     Blank = 0;
2103     Asterisk = 1;
2104     Zero = 2;
2105     const
2106     {$IFOPT N+}
2107     MaxPlaces = 18;
2108     {$ELSE}
2109     MaxPlaces = 11;
2110     {$ENDIF}
2111     FormChars : string[8] = '#@*$-+,.';
2112     PlusArray : array[Boolean] of AnsiChar = ('+', '-');
2113     MinusArray : array[Boolean] of AnsiChar = (' ', '-');
2114     FillArray : array[Blank..Zero] of AnsiChar = (' ', '*', '0');
2115     var
2116     S : ShortString; {temporary string}
2117     Filler : Integer; {char for unused digit slots: ' ', '*', '0'}
2118     WontFit, {true if number won't fit in the mask}
2119     AddMinus, {true if minus sign needs to be added}
2120     Dollar, {true if floating dollar sign is desired}
2121     Negative : Boolean; {true if B is negative}
2122     StartF, {starting point of the numeric field}
2123     EndF : Word; {end of numeric field}
2124     RtChars, {# of chars to add to right}
2125     LtChars, {# of chars to add to left}
2126     DotPos, {position of '.' in Mask}
2127     Digits, {total # of digits}
2128     Places, {# of digits after the '.'}
2129     Blanks, {# of blanks returned by Str}
2130     FirstDigit, {pos. of first digit returned by Str}
2131     Extras, {# of extra digits needed for special cases}
2132     DigitPtr : Byte; {pointer into temporary string of digits}
2133     I : Word;
2134     label
2135     EndFound,
2136     RedoCase,
2137     Done;
2138     begin
2139     {assume decimal point at end?}
2140     Result := Mask;
2141     if (not AssumeDP) and (not CharExistsS(Result, '.')) then
2142     AssumeDP := true;
2143     if AssumeDP and (Result <> '') and (Length(Result) < 255) then begin
2144     Inc(Result[0]);
2145     Result[Length(Result)] := '.';
2146     end;
2147    
2148     RtChars := 0;
2149     LtChars := 0;
2150    
2151     {check for empty string}
2152     if Length(Result) = 0 then
2153     goto Done;
2154    
2155     {initialize variables}
2156     Filler := Blank;
2157     DotPos := 0;
2158     Places := 0;
2159     Digits := 0;
2160     Dollar := False;
2161     AddMinus := True;
2162     StartF := 1;
2163    
2164     {store the sign of the real and make it positive}
2165     Negative := (R < 0);
2166     R := Abs(R);
2167    
2168     {strip and count c's}
2169     for I := Length(Result) downto 1 do begin
2170     if Result[I] = 'C' then begin
2171     Inc(RtChars);
2172     System.Delete(Result, I, 1);
2173     end else if Result[I] = 'c' then begin
2174     Inc(LtChars);
2175     System.Delete(Result, I, 1);
2176     end;
2177     end;
2178    
2179     {find the starting point for the field}
2180     while (StartF <= Length(Result)) and
2181     not CharExistsS(FormChars, Result[StartF]) do
2182     Inc(StartF);
2183     if StartF > Length(Result) then
2184     goto Done;
2185    
2186     {find the end point for the field}
2187     EndF := StartF;
2188     for I := StartF to Length(Result) do begin
2189     EndF := I;
2190     case Result[I] of
2191     '*' : Filler := Asterisk;
2192     '@' : Filler := Zero;
2193     '$' : Dollar := True;
2194     '-',
2195     '+' : AddMinus := False;
2196     '#' : {ignore} ;
2197     ',',
2198     '.' : DotPos := I;
2199     else
2200     goto EndFound;
2201     end;
2202     {Inc(EndF);}
2203     end;
2204    
2205     {if we get here at all, the last char was part of the field}
2206     Inc(EndF);
2207    
2208     EndFound:
2209     {if we jumped to here instead, it wasn't}
2210     Dec(EndF);
2211    
2212     {disallow Dollar if Filler is Zero}
2213     if Filler = Zero then
2214     Dollar := False;
2215    
2216     {we need an extra slot if Dollar is True}
2217     Extras := Ord(Dollar);
2218    
2219     {get total # of digits and # after the decimal point}
2220     for I := StartF to EndF do
2221     case Result[I] of
2222     '#', '@',
2223     '*', '$' :
2224     begin
2225     Inc(Digits);
2226     if (I > DotPos) and (DotPos <> 0) then
2227     Inc(Places);
2228     end;
2229     end;
2230    
2231     {need one more 'digit' if Places > 0}
2232     Inc(Digits, Ord(Places > 0));
2233    
2234     {also need an extra blank if (1) Negative is true, and (2) Filler is Blank,
2235     and (3) AddMinus is true}
2236     if Negative and AddMinus and (Filler = Blank) then
2237     Inc(Extras)
2238     else
2239     AddMinus := False;
2240    
2241     {translate the real to a string}
2242     Str(R:Digits:Places, S);
2243    
2244     {add zeros that Str may have left out}
2245     if Places > MaxPlaces then begin
2246     FillChar(S[Length(S)+1], Places-MaxPlaces, '0');
2247     inc(S[0], Places-MaxPlaces);
2248     while (Length(S) > Digits) and (S[1] = ' ') do
2249     System.Delete(S, 1, 1);
2250     end;
2251    
2252     {count number of initial blanks}
2253     Blanks := 1;
2254     while S[Blanks] = ' ' do
2255     Inc(Blanks);
2256     FirstDigit := Blanks;
2257     Dec(Blanks);
2258    
2259     {the number won't fit if (a) S is longer than Digits or (b) the number of
2260     initial blanks is less than Extras}
2261     WontFit := (Length(S) > Digits) or (Blanks < Extras);
2262    
2263     {if it won't fit, fill decimal slots with '*'}
2264     if WontFit then begin
2265     for I := StartF to EndF do
2266     case Result[I] of
2267     '#', '@', '*', '$' : Result[I] := '*';
2268     '+' : Result[I] := PlusArray[Negative];
2269     '-' : Result[I] := MinusArray[Negative];
2270     end;
2271     goto Done;
2272     end;
2273    
2274     {fill initial blanks in S with Filler; insert floating dollar sign}
2275     if Blanks > 0 then begin
2276     FillChar(S[1], Blanks, FillArray[Filler]);
2277    
2278     {put floating dollar sign in last blank slot if necessary}
2279     if Dollar then begin
2280     S[Blanks] := LtCurr[1];
2281     Dec(Blanks);
2282     end;
2283    
2284     {insert a minus sign if necessary}
2285     if AddMinus then
2286     S[Blanks] := '-';
2287     end;
2288    
2289     {put in the digits / signs}
2290     DigitPtr := Length(S);
2291     for I := EndF downto StartF do begin
2292     RedoCase:
2293     case Result[I] of
2294     '#', '@', '*', '$' :
2295     if DigitPtr <> 0 then begin
2296     Result[I] := S[DigitPtr];
2297     Dec(DigitPtr);
2298     if (DigitPtr <> 0) and (S[DigitPtr] = '.') then {!!.01}
2299     Dec(DigitPtr);
2300     end
2301     else
2302     Result[I] := FillArray[Filler];
2303     ',' :
2304     begin
2305     Result[I] := Sep;
2306     if (I < DotPos) and (DigitPtr < FirstDigit) then begin
2307     Result[I] := '#';
2308     goto RedoCase;
2309     end;
2310     end;
2311     '.' :
2312     begin
2313     Result[I] := DecPt;
2314     if (I < DotPos) and (DigitPtr < FirstDigit) then begin
2315     Result[I] := '#';
2316     goto RedoCase;
2317     end;
2318     end;
2319     '+' : Result[I] := PlusArray[Negative];
2320     '-' : Result[I] := MinusArray[Negative];
2321     end;
2322     end;
2323    
2324     Done:
2325     if AssumeDP then
2326     Dec(Result[0]);
2327     if RtChars > 0 then begin
2328     S := RtCurr;
2329     if Byte(S[0]) > RtChars then
2330     S[0] := AnsiChar(RtChars)
2331     else
2332     S := LeftPadS(S, RtChars);
2333     Result := Result + S;
2334     end;
2335     if LtChars > 0 then begin
2336     S := LtCurr;
2337     if Byte(S[0]) > LtChars then
2338     S[0] := AnsiChar(LtChars)
2339     else
2340     S := PadS(S, LtChars);
2341     Result := S + Result;
2342     end;
2343     end;
2344    
2345     function FloatFormS(const Mask : ShortString ; R : TstFloat ; const LtCurr,
2346     RtCurr : ShortString ; Sep, DecPt : AnsiChar) : ShortString;
2347     {-Return a formatted string with digits from R merged into mask.}
2348     begin
2349     Result := FormPrimS(Mask, R, LtCurr, RtCurr, Sep, DecPt, False);
2350     end;
2351    
2352     function LongIntFormS(const Mask : ShortString ; L : LongInt ; const LtCurr,
2353     RtCurr : ShortString ; Sep : AnsiChar) : ShortString;
2354     {-Return a formatted string with digits from L merged into mask.}
2355     begin
2356     Result := FormPrimS(Mask, L, LtCurr, RtCurr, Sep, '.', True);
2357     end;
2358    
2359     function StrChPosS(const P : String; C : Char; var Pos : Cardinal) : Boolean;
2360     var
2361     I: Integer;
2362     {-Return the position of a specified character within a string.}
2363     begin
2364     Result := False;
2365     for I := 1 to Length(P) do
2366     begin
2367     if P[I] = C then
2368     begin
2369     Result := True;
2370     Pos := I;
2371     Break;
2372     end;
2373     end;
2374     end;
2375    
2376     function StrChPosS(const P : ShortString; C : AnsiChar; var Pos : Cardinal) : Boolean;
2377     {-Return the position of a specified character within a string.}
2378     asm
2379     push ebx { Save registers }
2380     push edi
2381    
2382     xor edi, edi { Zero counter }
2383     xor ebx, ebx
2384     add bl, [eax] { Get input length }
2385     jz @@NotFound
2386     inc eax
2387    
2388     @@Loop:
2389     inc edi { Increment counter }
2390     cmp [eax], dl { Did we find it? }
2391     jz @@Found
2392     inc eax { Increment pointer }
2393    
2394     cmp edi, ebx { End of string? }
2395     jnz @@Loop { If not, loop }
2396    
2397     @@NotFound:
2398     xor eax, eax { Not found, zero EAX for False }
2399     mov [ecx], eax
2400     jmp @@Done
2401    
2402     @@Found:
2403     mov [ecx], edi { Set Pos }
2404     mov eax, 1 { Set EAX to True }
2405    
2406     @@Done:
2407     pop edi { Restore registers }
2408     pop ebx
2409     end;
2410    
2411     function StrStPosS(const P, S : ShortString; var Pos : Cardinal) : Boolean;
2412     {-Return the position of a specified substring within a string.}
2413     begin
2414     Pos := System.Pos(S, P);
2415     Result := Pos <> 0;
2416     end;
2417    
2418     function StrStCopyS(const S : ShortString; Pos, Count : Cardinal) : ShortString;
2419     {-Copy characters at a specified position in a string.}
2420     begin
2421     Result := System.Copy(S, Pos, Count);
2422     end;
2423    
2424     function StrChInsertS(const S : ShortString; C : AnsiChar; Pos : Cardinal) : ShortString;
2425     {-Insert a character into a string at a specified position.}
2426     var
2427     Temp : string[2];
2428     begin
2429     Temp[0] := #1;
2430     Temp[1] := C;
2431     Result := S;
2432     System.Insert(Temp, Result, Pos);
2433     end;
2434    
2435     function StrStInsertS(const S1, S2 : ShortString; Pos : Cardinal) : ShortString;
2436     {-Insert a string into another string at a specified position.}
2437     begin
2438     Result := S1;
2439     System.Insert(S2, Result, Pos);
2440     end;
2441    
2442     function StrChDeleteS(const S : ShortString; Pos : Cardinal) : ShortString;
2443     {-Delete the character at a specified position in a string.}
2444     begin
2445     Result := S;
2446     System.Delete(Result, Pos, 1);
2447     end;
2448    
2449     function StrStDeleteS(const S : ShortString; Pos, Count : Cardinal) : ShortString;
2450     {-Delete characters at a specified position in a string.}
2451     begin
2452     Result := S;
2453     System.Delete(Result, Pos, Count);
2454     end;
2455    
2456     {----------------------------- NEW FUNCTIONS (3.00) -------------------------}
2457    
2458     function CopyLeftS(const S : ShortString; Len : Cardinal) : ShortString;
2459     {-Return the left Len characters of a string}
2460     begin
2461     if (Len < 1) or (S = '') then
2462     Result := ''
2463     else
2464     Result := Copy(S, 1, Len);
2465     end;
2466    
2467    
2468    
2469     function CopyMidS(const S : ShortString; First, Len : Cardinal) : ShortString;
2470     {-Return the mid part of a string}
2471     begin
2472     if (First > Length(S)) or (Len < 1) or (S = '') then
2473     Result := ''
2474     else
2475     Result := Copy(S, First, Len);
2476     end;
2477    
2478    
2479    
2480     function CopyRightS(const S : ShortString; First : Cardinal) : ShortString;
2481     {-Return the right Len characters of a string}
2482     begin
2483     if (First > Length(S)) or (First < 1) or (S = '') then
2484     Result := ''
2485     else
2486     Result := Copy(S, First, Length(S));
2487     end;
2488    
2489     function CopyRightAbsS(const S : ShortString; NumChars : Cardinal) : ShortString;
2490     {-Return NumChar characters starting from end}
2491     begin
2492     if (Length(S) > NumChars) then
2493     Result := Copy(S, (Length(S) - NumChars)+1, NumChars)
2494     else
2495     Result := S;
2496     end;
2497    
2498    
2499     function CopyFromNthWordS(const S, WordDelims : ShortString;
2500     const AWord : ShortString; N : Cardinal; {!!.02}
2501     var SubString : ShortString) : Boolean;
2502     var
2503     P : Cardinal;
2504     begin
2505     if (WordPosS(S, WordDelims, AWord, N, P)) then begin
2506     SubString := Copy(S, P, Length(S));
2507     Result := True;
2508     end else begin
2509     SubString := '';
2510     Result := False;
2511     end;
2512     end;
2513    
2514    
2515    
2516     function DeleteFromNthWordS(const S, WordDelims : ShortString;
2517     AWord : ShortString; N : Cardinal;
2518     var SubString : ShortString) : Boolean;
2519     var
2520     P : Cardinal;
2521     begin
2522     if (WordPosS(S, WordDelims, AWord, N, P)) then begin
2523     Result := True;
2524     SubString := Copy(S, 1, P-1);
2525     end else begin
2526     Result := False;
2527     SubString := '';
2528     end;
2529     end;
2530    
2531    
2532    
2533     function CopyFromToWordS(const S, WordDelims, Word1, Word2 : ShortString;
2534     N1, N2 : Cardinal;
2535     var SubString : ShortString) : Boolean;
2536     var
2537     P1,
2538     P2 : Cardinal;
2539     begin
2540     if (WordPosS(S, WordDelims, Word1, N1, P1)) then begin
2541     if (WordPosS(S, WordDelims, Word2, N2, P2)) then begin
2542     Dec(P2);
2543     if (P2 > P1) then begin
2544     Result := True;
2545     SubString := Copy(S, P1, P2-P1);
2546     end else begin
2547     Result := False;
2548     SubString := '';
2549     end;
2550     end else begin
2551     Result := False;
2552     SubString := '';
2553     end;
2554     end else begin
2555     Result := False;
2556     SubString := '';
2557     end;
2558     end;
2559    
2560    
2561    
2562     function DeleteFromToWordS(const S, WordDelims, Word1, Word2 : ShortString;
2563     N1, N2 : Cardinal;
2564     var SubString : ShortString) : Boolean;
2565     var
2566     P1,
2567     P2 : Cardinal;
2568     begin
2569     SubString := S;
2570     if (WordPosS(S, WordDelims, Word1, N1, P1)) then begin
2571     if (WordPosS(S, WordDelims, Word2, N2, P2)) then begin
2572     Dec(P2);
2573     if (P2 > P1) then begin
2574     Result := True;
2575     System.Delete(SubString, P1, P2-P1+1);
2576     end else begin
2577     Result := False;
2578     SubString := '';
2579     end;
2580     end else begin
2581     Result := False;
2582     SubString := '';
2583     end;
2584     end else begin
2585     Result := False;
2586     SubString := '';
2587     end;
2588     end;
2589    
2590    
2591    
2592     function CopyWithinS(const S, Delimiter : ShortString;
2593     Strip : Boolean) : ShortString;
2594     var
2595     P1,
2596     P2 : Cardinal;
2597     TmpStr : ShortString;
2598     begin
2599     if (S = '') or (Delimiter = '') or (Pos(Delimiter, S) = 0) then
2600     Result := ''
2601     else begin
2602     if (StrStPosS(S, Delimiter, P1)) then begin
2603     TmpStr := Copy(S, P1 + Length(Delimiter), Length(S));
2604     if StrStPosS(TmpStr, Delimiter, P2) then begin
2605     Result := Copy(TmpStr, 1, P2-1);
2606     if (not Strip) then
2607     Result := Delimiter + Result + Delimiter;
2608     end else begin
2609     Result := TmpStr;
2610     if (not Strip) then
2611     Result := Delimiter + Result;
2612     end;
2613     end;
2614     end;
2615     end;
2616    
2617    
2618    
2619     function DeleteWithinS(const S, Delimiter : ShortString) : ShortString;
2620     var
2621     P1,
2622     P2 : Cardinal;
2623     TmpStr : ShortString;
2624     begin
2625     if (S = '') or (Delimiter = '') or (Pos(Delimiter, S) = 0) then
2626     Result := ''
2627     else begin
2628     if (StrStPosS(S, Delimiter, P1)) then begin
2629     TmpStr := Copy(S, P1 + Length(Delimiter), Length(S));
2630     if (Pos(Delimiter, TmpStr) = 0) then
2631     Result := Copy(S, 1, P1-1)
2632     else begin
2633     if (StrStPosS(TmpStr, Delimiter, P2)) then begin
2634     Result := S;
2635     P2 := P2 + (2*Length(Delimiter));
2636     System.Delete(Result, P1, P2);
2637     end;
2638     end;
2639     end;
2640     end;
2641     end;
2642    
2643    
2644    
2645     function ReplaceWordS(const S, WordDelims, OldWord, NewWord : ShortString;
2646     N : Cardinal;
2647     var Replacements : Cardinal) : ShortString;
2648     var
2649     I,
2650     C,
2651     P1 : Cardinal;
2652     begin
2653     if (S = '') or (WordDelims = '') or (OldWord = '') or
2654     (Pos(OldWord, S) = 0) then begin
2655     Result := S;
2656     Replacements := 0;
2657     end else begin
2658     if (WordPosS(S, WordDelims, OldWord, N, P1)) then begin
2659     Result := S;
2660     System.Delete(Result, P1, Length(OldWord));
2661     C := 0;
2662     for I := 1 to Replacements do begin
2663     if ((Length(NewWord) + Length(Result)) <= 255) then begin
2664     Inc(C);
2665     System.Insert(NewWord, Result, P1);
2666     Inc(P1, Length(NewWord) + 1);
2667     end else begin
2668     Replacements := C;
2669     Exit;
2670     end;
2671     end;
2672     end else begin
2673     Result := S;
2674     Replacements := 0;
2675     end;
2676     end;
2677     end;
2678    
2679    
2680     function ReplaceWordAllS(const S, WordDelims, OldWord, NewWord : ShortString;
2681     var Replacements : Cardinal) : ShortString;
2682     var
2683     I,
2684     C,
2685     P1 : Cardinal;
2686     begin
2687     if (S = '') or (WordDelims = '') or (OldWord = '') or
2688     (Pos(OldWord, S) = 0) then begin
2689     Result := S;
2690     Replacements := 0;
2691     end else begin
2692     Result := S;
2693     C := 0;
2694     while (WordPosS(Result, WordDelims, OldWord, 1, P1)) do begin
2695     System.Delete(Result, P1, Length(OldWord));
2696     for I := 1 to Replacements do begin
2697     if ((Length(NewWord) + Length(Result)) <= 255) then begin
2698     Inc(C);
2699     System.Insert(NewWord, Result, P1);
2700     end else begin
2701     Replacements := C;
2702     Exit;
2703     end;
2704     end;
2705     end;
2706     Replacements := C;
2707     end;
2708     end;
2709    
2710    
2711     function ReplaceStringS(const S, OldString, NewString : ShortString;
2712     N : Cardinal;
2713     var Replacements : Cardinal) : ShortString;
2714     var
2715     I,
2716     C,
2717     P1 : Cardinal;
2718     TmpStr : ShortString;
2719     begin
2720     if (S = '') or (OldString = '') or (Pos(OldString, S) = 0) then begin
2721     Result := S;
2722     Replacements := 0;
2723     Exit;
2724     end;
2725     TmpStr := S;
2726    
2727     I := 1;
2728     P1 := Pos(OldString, TmpStr);
2729     C := P1;
2730     while (I < N) and (C < Length(TmpStr)) do begin
2731     Inc(I);
2732     System.Delete(TmpStr, 1, P1 + Length(OldString));
2733     Inc(C, P1 + Length(OldString));
2734     end;
2735     Result := S;
2736     System.Delete(Result, C, Length(OldString));
2737    
2738     C := 0;
2739     for I := 1 to Replacements do begin
2740     if ((Length(NewString) + Length(Result)) <= 255) then begin
2741     Inc(C);
2742     System.Insert(NewString, Result, P1);
2743     Inc(P1, Length(NewString) + 1);
2744     end else begin
2745     Replacements := C;
2746     Exit;
2747     end;
2748     end;
2749     end;
2750    
2751    
2752     function ReplaceStringAllS(const S, OldString, NewString : ShortString;
2753     var Replacements : Cardinal) : ShortString;
2754     var
2755     I,
2756     C,
2757     P1 : Cardinal;
2758     Tmp: String;
2759     begin
2760     Result := S;
2761     if (S = '') or (OldString = '') or (Pos(OldString, S) = 0) then
2762     Replacements := 0
2763     else begin
2764     Tmp := S;
2765     P1 := Pos(OldString, S);
2766     if (P1 > 0) then begin
2767     Result := Copy(Tmp, 1, P1-1);
2768     C := 0;
2769     while (P1 > 0) do begin
2770     for I := 1 to Replacements do begin
2771     Inc(C);
2772     Result := Result + NewString;
2773     end;
2774     Tmp := Copy(Tmp, P1+Length(OldString), MaxInt);
2775     P1 := Pos(OldString, Tmp);
2776     if (P1 > 0) then begin
2777     Result := Result + Copy(Tmp, 1, P1-1);
2778     {Tmp := Copy(Tmp, P1, MaxInt)};
2779     end else
2780     Result := Result + Tmp;
2781     end;
2782     Replacements := C;
2783     end else begin
2784     Result := S;
2785     Replacements := 0;
2786     end;
2787     end;
2788     end;
2789    
2790     function LastWordS(const S, WordDelims, AWord : ShortString;
2791     var Position : Cardinal) : Boolean;
2792     var
2793     TmpStr : ShortString;
2794     I : Cardinal;
2795     begin
2796     if (S = '') or (WordDelims = '') or
2797     (AWord = '') or (Pos(AWord, S) = 0) then begin
2798     Result := False;
2799     Position := 0;
2800     Exit;
2801     end;
2802    
2803     TmpStr := S;
2804     I := Length(TmpStr);
2805     while (Pos(TmpStr[I], WordDelims) > 0) do begin
2806     System.Delete(TmpStr, I, 1);
2807     I := Length(TmpStr);
2808     end;
2809    
2810     Position := Length(TmpStr);
2811     repeat
2812     while (Pos(TmpStr[Position], WordDelims) = 0) and (Position > 1) do
2813     Dec(Position);
2814     if (Copy(TmpStr, Position + 1, Length(AWord)) = AWord) then begin
2815     Inc(Position);
2816     Result := True;
2817     Exit;
2818     end;
2819     System.Delete(TmpStr, Position, Length(TmpStr));
2820     Position := Length(TmpStr);
2821     until (Length(TmpStr) = 0);
2822     Result := False;
2823     Position := 0;
2824     end;
2825    
2826    
2827    
2828     function LastWordAbsS(const S, WordDelims : ShortString;
2829     var Position : Cardinal) : Boolean;
2830     begin
2831     if (S = '') or (WordDelims = '') then begin
2832     Result := False;
2833     Position := 0;
2834     Exit;
2835     end;
2836    
2837     {find first non-delimiter character, if any. If not a "one-word wonder"}
2838     Position := Length(S);
2839     while (Position > 0) and (Pos(S[Position], WordDelims) > 0) do
2840     Dec(Position);
2841    
2842     if (Position = 0) then begin
2843     Result := True;
2844     Position := 1;
2845     Exit;
2846     end;
2847    
2848     {find next delimiter character}
2849     while (Position > 0) and (Pos(S[Position], WordDelims) = 0) do
2850     Dec(Position);
2851     Inc(Position);
2852     Result := True;
2853     end;
2854    
2855    
2856    
2857     function LastStringS(const S, AString : ShortString;
2858     var Position : Cardinal) : Boolean;
2859     var
2860     TmpStr : ShortString;
2861     I, C : Cardinal;
2862     begin
2863     if (S = '') or (AString = '') or (Pos(AString, S) = 0) then begin
2864     Result := False;
2865     Position := 0;
2866     Exit;
2867     end;
2868    
2869     TmpStr := S;
2870     C := 0;
2871     I := Pos(AString, TmpStr);
2872     while (I > 0) do begin
2873     Inc(C, I + Length(AString));
2874     System.Delete(TmpStr, 1, I + Length(AString));
2875     I := Pos(AString, TmpStr);
2876     end;
2877     {Go back the length of AString since the while loop deletes the last instance}
2878     Dec(C, Length(AString));
2879     Position := C;
2880     Result := True;
2881     end;
2882    
2883    
2884    
2885     function KeepCharsS(const S, Chars : ShortString) : ShortString;
2886     var
2887     FromInx : Cardinal;
2888     ToInx : Cardinal;
2889     begin
2890     {if either the input string or the list of acceptable chars is empty
2891     the destination string will also be empty}
2892     if (S = '') or (Chars = '') then begin
2893     Result := '';
2894     Exit;
2895     end;
2896    
2897     {set the maximum length of the result string (it could be less than
2898     this, of course}
2899     Result[0] := AnsiChar(length(S));
2900    
2901     {start off the to index}
2902     ToInx := 0;
2903    
2904     {in a loop, copy over the chars that match the list}
2905     for FromInx := 1 to length(S) do
2906     if CharExistsS(Chars, S[FromInx]) then begin
2907     inc(ToInx);
2908     Result[ToInx] := S[FromInx];
2909     end;
2910    
2911     {make sure that the length of the result string is correct}
2912     Result[0] := AnsiChar(ToInx);
2913     end;
2914    
2915    
2916    
2917     function RepeatStringS(const RepeatString : ShortString;
2918     var Repetitions : Cardinal;
2919     MaxLen : Cardinal) : ShortString;
2920     var
2921     i : Cardinal;
2922     Len : Cardinal;
2923     ActualReps : Cardinal;
2924     begin
2925     Result := '';
2926     if (MaxLen <> 0) and
2927     (Repetitions <> 0) and
2928     (RepeatString <> '') then begin
2929     if (MaxLen > 255) then
2930     MaxLen := 255;
2931     Len := length(RepeatString);
2932     ActualReps := MaxLen div Len;
2933     if (ActualReps > Repetitions) then
2934     ActualReps := Repetitions
2935     else
2936     Repetitions := ActualReps;
2937     if (ActualReps > 0) then begin
2938     Result[0] := AnsiChar(ActualReps * Len);
2939     for i := 0 to pred(ActualReps) do
2940     Move(RepeatString[1], Result[i * Len + 1], Len);
2941     end;
2942     end;
2943     end;
2944    
2945    
2946    
2947     function TrimCharsS(const S, Chars : ShortString) : ShortString;
2948     begin
2949     Result := RightTrimCharsS(S, Chars);
2950     Result := LeftTrimCharsS(Result, Chars);
2951     end;
2952    
2953    
2954    
2955     function RightTrimCharsS(const S, Chars : ShortString) : ShortString;
2956     var
2957     CutOff : integer;
2958     begin
2959     CutOff := length(S);
2960     while (CutOff > 0) do begin
2961     if not CharExistsS(Chars, S[CutOff]) then
2962     Break;
2963     dec(CutOff);
2964     end;
2965     if (CutOff = 0) then
2966     Result := ''
2967     else
2968     Result := Copy(S, 1, CutOff);
2969     end;
2970    
2971    
2972    
2973     function LeftTrimCharsS(const S, Chars : ShortString) : ShortString;
2974     var
2975     CutOff : integer;
2976     LenS : integer;
2977     begin
2978     LenS := length(S);
2979     CutOff := 1;
2980     while (CutOff <= LenS) do begin
2981     if not CharExistsS(Chars, S[CutOff]) then
2982     Break;
2983     inc(CutOff);
2984     end;
2985     if (CutOff > LenS) then
2986     Result := ''
2987     else
2988     Result := Copy(S, CutOff, LenS - CutOff + 1);
2989     end;
2990    
2991    
2992    
2993     function ExtractTokensS(const S, Delims : ShortString;
2994     QuoteChar : AnsiChar;
2995     AllowNulls : Boolean;
2996     Tokens : TStrings) : Cardinal;
2997     var
2998     State : (ScanStart,
2999     ScanQuotedToken,
3000     ScanQuotedTokenEnd,
3001     ScanNormalToken,
3002     ScanNormalTokenWithQuote);
3003     CurChar : AnsiChar;
3004     TokenStart : integer;
3005     Inx : integer;
3006     begin
3007     {Notes: this routine implements the following state machine
3008     start ----> ScanStart
3009     ScanStart-----quote----->ScanQuotedToken
3010     ScanStart-----delim----->ScanStart (1)
3011     ScanStart-----other----->ScanNormalToken
3012     ScanQuotedToken-----quote----->ScanQuotedTokenEnd
3013     ScanQuotedToken-----other----->ScanQuotedToken
3014     ScanQuotedTokenEnd-----quote----->ScanNormalTokenWithQuote
3015     ScanQuotedTokenEnd-----delim----->ScanStart (2)
3016     ScanQuotedTokenEnd-----other----->ScanNormalToken
3017     ScanNormalToken-----quote----->ScanNormalTokenWithQuote
3018     ScanNormalToken-----delim----->ScanStart (3)
3019     ScanNormalToken-----other----->ScanNormalToken
3020     ScanNormalTokenWithQuote-----quote----->ScanNormalTokenWithQuote
3021     ScanNormalTokenWithQuote-----other----->ScanNormalToken
3022    
3023     (1) output a null token if allowed
3024     (2) output a token, stripping quotes (if the dequoted token is
3025     empty, output a null token if allowed)
3026     (3) output a token; no quote stripping
3027    
3028     If the quote character is #0, it's taken to mean that the routine
3029     should not check for quoted substrings.}
3030    
3031     {clear the tokens string list, set the return value to zero}
3032     Tokens.Clear;
3033     Result := 0;
3034    
3035     {if the input string is empty or the delimiter list is empty or
3036     the quote character is found in the delimiter list, return zero
3037     tokens found}
3038     if (S = '') or
3039     (Delims = '') or
3040     CharExistsS(Delims, QuoteChar) then
3041     Exit;
3042    
3043     {start off in the normal scanning state}
3044     State := ScanStart;
3045    
3046     {the first token starts at position 1}
3047     TokenStart := 1;
3048    
3049     {read through the entire string}
3050     for Inx := 1 to length(S) do begin
3051    
3052     {get the current character}
3053     CurChar := S[Inx];
3054    
3055     {process the character according to the current state}
3056     case State of
3057     ScanStart :
3058     begin
3059     {if the current char is the quote character, switch states}
3060     if (QuoteChar <> #0) and (CurChar = QuoteChar) then
3061     State := ScanQuotedToken
3062    
3063     {if the current char is a delimiter, output a null token}
3064     else if CharExistsS(Delims, CurChar) then begin
3065    
3066     {if allowed to, output a null token}
3067     if AllowNulls then begin
3068     Tokens.Add('');
3069     inc(Result);
3070     end;
3071    
3072     {set the start of the next token to be one character after
3073     this delimiter}
3074     TokenStart := succ(Inx);
3075     end
3076    
3077     {otherwise, the current char is starting a normal token, so
3078     switch states}
3079     else
3080     State := ScanNormalToken
3081     end;
3082    
3083     ScanQuotedToken :
3084     begin
3085     {if the current char is the quote character, switch states}
3086     if (CurChar = QuoteChar) then
3087     State := ScanQuotedTokenEnd
3088     end;
3089    
3090     ScanQuotedTokenEnd :
3091     begin
3092     {if the current char is the quote character, we have a token
3093     consisting of two (or more) quoted substrings, so switch
3094     states}
3095     if (CurChar = QuoteChar) then
3096     State := ScanNormalTokenWithQuote
3097    
3098     {if the current char is a delimiter, output the token
3099     without the quotes}
3100     else if CharExistsS(Delims, CurChar) then begin
3101    
3102     {if the token is empty without the quotes, output a null
3103     token only if allowed to}
3104     if ((Inx - TokenStart) = 2) then begin
3105     if AllowNulls then begin
3106     Tokens.Add('');
3107     inc(Result);
3108     end
3109     end
3110    
3111     {else output the token without the quotes}
3112     else begin
3113     Tokens.Add(Copy(S, succ(TokenStart), Inx - TokenStart - 2));
3114     inc(Result);
3115     end;
3116    
3117     {set the start of the next token to be one character after
3118     this delimiter}
3119     TokenStart := succ(Inx);
3120    
3121     {switch states back to the start state}
3122     State := ScanStart;
3123     end
3124    
3125     {otherwise it's a (complex) normal token, so switch states}
3126     else
3127     State := ScanNormalToken
3128     end;
3129    
3130     ScanNormalToken :
3131     begin
3132     {if the current char is the quote character, we have a
3133     complex token with at least one quoted substring, so switch
3134     states}
3135     if (QuoteChar <> #0) and (CurChar = QuoteChar) then
3136     State := ScanNormalTokenWithQuote
3137    
3138     {if the current char is a delimiter, output the token}
3139     else if CharExistsS(Delims, CurChar) then begin
3140     Tokens.Add(Copy(S, TokenStart, Inx - TokenStart));
3141     inc(Result);
3142    
3143     {set the start of the next token to be one character after
3144     this delimiter}
3145     TokenStart := succ(Inx);
3146    
3147     {switch states back to the start state}
3148     State := ScanStart;
3149     end;
3150     end;
3151    
3152     ScanNormalTokenWithQuote :
3153     begin
3154     {if the current char is the quote character, switch states
3155     back to scanning a normal token}
3156     if (CurChar = QuoteChar) then
3157     State := ScanNormalToken;
3158     end;
3159    
3160     end;
3161     end;
3162    
3163     {we need to process the (possible) final token: first assume that
3164     the current character index is just beyond the end of the string}
3165     Inx := succ(length(S));
3166    
3167     {if we are in the scanning quoted token state, we've read an opening
3168     quote, but no closing one; increment the token start value}
3169     if (State = ScanQuotedToken) then
3170     inc(TokenStart)
3171    
3172     {if we've finished scanning a quoted token, we've read both quotes;
3173     increment the token start value, and decrement the current index}
3174     else if (State = ScanQuotedTokenEnd) then begin
3175     inc(TokenStart);
3176     dec(Inx);
3177     end;
3178    
3179     {if the final token is not empty, output the token}
3180     if (TokenStart < Inx) then begin
3181     Tokens.Add(Copy(S, TokenStart, Inx - TokenStart));
3182     inc(Result);
3183     end
3184     {otherwise the final token is empty, so output a null token if
3185     allowed to}
3186     else if AllowNulls then begin
3187     Tokens.Add('');
3188     inc(Result);
3189     end;
3190     end;
3191    
3192    
3193    
3194     function ContainsOnlyS(const S, Chars : ShortString;
3195     var BadPos : Cardinal) : Boolean;
3196     var
3197     I : Cardinal;
3198     begin
3199     if (S = '') then begin
3200     Result := False;
3201     BadPos := 0;
3202     end else begin
3203     for I := 1 to Length(S) do begin
3204     if (not CharExistsS(Chars, S[I])) then begin
3205     BadPos := I;
3206     Result := False;
3207     Exit;
3208     end;
3209     end;
3210     Result := True;
3211     BadPos := 0;
3212     end;
3213     end;
3214    
3215    
3216    
3217     function ContainsOtherThanS(const S, Chars : ShortString;
3218     var BadPos : Cardinal) : Boolean;
3219     var
3220     I : Cardinal;
3221     begin
3222     if (S = '') then begin
3223     Result := False;
3224     BadPos := 0;
3225     end else begin
3226     for I := 1 to Length(S) do begin
3227     if (CharExistsS(Chars, S[I])) then begin
3228     BadPos := I;
3229     Result := True;
3230     Exit;
3231     end;
3232     end;
3233     Result := False;
3234     BadPos := 0;
3235     end;
3236     end;
3237    
3238    
3239    
3240     function IsChAlphaS(C : Char) : Boolean;
3241     {-Returns true if Ch is an alpha}
3242     begin
3243     Result := Windows.IsCharAlpha(C);
3244     end;
3245    
3246    
3247    
3248     function IsChNumericS(C : AnsiChar; const Numbers : ShortString) : Boolean;
3249     {-Returns true if Ch in numeric set}
3250     begin
3251     Result := CharExistsS(Numbers, C);
3252     end;
3253    
3254    
3255     function IsChAlphaNumericS(C : Char; const Numbers : ShortString) : Boolean;
3256     {-Returns true if Ch is an alpha or numeric}
3257     begin
3258     Result := Windows.IsCharAlpha(C) or CharExistsS(Numbers, C);
3259     end;
3260    
3261    
3262    
3263     function IsStrAlphaS(const S : string) : Boolean;
3264     {-Returns true if all characters in string are an alpha}
3265     var
3266     I : Cardinal;
3267     begin
3268     Result := false;
3269     if (length(S) > 0) then begin
3270     for I := 1 to Length(S) do
3271     if not Windows.IsCharAlpha(S[I]) then
3272     Exit;
3273     Result := true;
3274     end;
3275     end;
3276    
3277    
3278    
3279     function IsStrNumericS(const S, Numbers : ShortString) : Boolean;
3280     {-Returns true if all characters in string are in numeric set}
3281     var
3282     i : Cardinal;
3283     begin
3284     Result := false;
3285     if (length(S) > 0) then begin
3286     for i := 1 to Length(S) do
3287     if not CharExistsS(Numbers, S[i]) then
3288     Exit;
3289     Result := true;
3290     end;
3291     end;
3292    
3293    
3294     function IsStrAlphaNumericS(const S, Numbers : String) : Boolean;
3295     {-Returns true if all characters in string are alpha or numeric}
3296     var
3297     i : Cardinal;
3298     begin
3299     Result := false;
3300     if (length(S) > 0) then begin
3301     for I := 1 to Length(S) do
3302     if (not Windows.IsCharAlpha(S[i])) and
3303     (not CharExistsS(Numbers, S[i])) then
3304     Exit;
3305     Result := true;
3306     end;
3307     end;
3308    
3309     function StrWithinS(const S, SearchStr : ShortString;
3310     Start : Cardinal;
3311     var Position : Cardinal) : boolean;
3312     var
3313     TmpStr : ShortString;
3314     begin
3315     TmpStr := S;
3316     if (Start > 1) then
3317     System.Delete(TmpStr, 1, Start-1);
3318     Position := pos(SearchStr, TmpStr);
3319     if (Position > 0) then begin
3320     Position := Position + Start - 1;
3321     Result := True;
3322     end else
3323     Result := False;
3324     end;
3325    
3326    
3327     function WordPosS(const S, WordDelims, AWord : ShortString;
3328     N : Cardinal; var Position : Cardinal) : Boolean;
3329     {-returns the Nth instance of a given word within a string}
3330     var
3331     TmpStr : ShortString;
3332     Len,
3333     I,
3334     P1,
3335     P2 : Cardinal;
3336     begin
3337     if (S = '') or (AWord = '') or (Pos(AWord, S) = 0) or (N < 1) then begin
3338     Result := False;
3339     Position := 0;
3340     Exit;
3341     end;
3342    
3343     Result := False;
3344     Position := 0;
3345    
3346     TmpStr := S;
3347     I := 0;
3348     Len := Length(AWord);
3349     P1 := Pos(AWord, TmpStr);
3350    
3351     while (P1 > 0) and (Length(TmpStr) > 0) do begin
3352     P2 := P1 + pred(Len);
3353     if (P1 = 1) then begin
3354     if (Pos(TmpStr[P2+1], WordDelims) > 0) then begin
3355     Inc(I);
3356     end else
3357     System.Delete(TmpStr, 1, P2);
3358     end else if (Pos(TmpStr[P1-1], WordDelims) > 0) and
3359     ((Pos(TmpStr[P2+1], WordDelims) > 0) or
3360     (P2+1 = Length(TmpStr))) then begin
3361     Inc(I);
3362     end else if ((P1 + pred(Len)) = Length(TmpStr)) then begin
3363     if (P1 > 1) and (Pos(TmpStr[P1-1], WordDelims) > 0) then
3364     Inc(I);
3365     end;
3366    
3367     if (I = N) then begin
3368     Result := True;
3369     Position := Position + P1;
3370     Exit;
3371     end;
3372     System.Delete(TmpStr, 1, P2);
3373     Position := Position + P2;
3374     P1 := Pos(AWord, TmpStr);
3375     end;
3376     end;
3377    
3378    
3379     end.

  ViewVC Help
Powered by ViewVC 1.1.20