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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2671 - (show annotations) (download)
Tue Aug 25 18:15:15 2015 UTC (8 years, 9 months ago) by torben
File size: 100553 byte(s)
Added tpsystools component
1 (* ***** 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