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

Contents of /dao/DelphiScanner/Components/tpsystools_4.04/source/StRegEx.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: 76168 byte(s)
Added tpsystools component
1 // TODO-UNICODE
2
3 (* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1
5 *
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
10 *
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
15 *
16 * The Original Code is TurboPower SysTools
17 *
18 * The Initial Developer of the Original Code is
19 * TurboPower Software
20 *
21 * Portions created by the Initial Developer are Copyright (C) 1996-2002
22 * the Initial Developer. All Rights Reserved.
23 *
24 * Contributor(s):
25 *
26 * ***** END LICENSE BLOCK ***** *)
27
28 {*********************************************************}
29 {* SysTools: StRegEx.pas 4.04 *}
30 {*********************************************************}
31 {* SysTools: SysTools Regular Expression Engine *}
32 {*********************************************************}
33
34 {$I StDefine.inc}
35
36 unit StRegEx;
37
38 interface
39
40 uses
41 Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
42
43 StConst,
44 StBase,
45 StStrms;
46
47 const
48 StWordDelimString : string[31] = #9#32'!"&()*+,-./:;<=>?@[\]^`{|}~';
49 StHexDigitString : string[19] = '0123456789ABCDEF';
50
51 type
52 TMatchPosition = packed record
53 StartPos : Cardinal;
54 EndPos : Cardinal;
55 Length : Cardinal;
56 LineNum : Cardinal;
57 end;
58
59 TStOutputOption = (ooUnselected, ooModified, ooCountOnly);
60 TStOutputOptions = set of TStOutputOption;
61
62 TStTokens = (tknNil, tknLitChar, tknCharClass, tknNegCharClass,
63 tknClosure, tknMaybeOne, tknAnyChar, tknBegOfLine,
64 tknEndOfLine, tknGroup, tknBegTag, tknEndTag, tknDitto);
65
66 PStPatRecord = ^TStPatRecord;
67 TStPatRecord = packed record
68 StrPtr : ^ShortString;
69 NestedPattern : PStPatRecord;
70 NextPattern : PStPatRecord;
71 Token : TStTokens;
72 OneChar : AnsiChar;
73 NextOK : Boolean;
74 end;
75
76 TStTagLevel = -1..9;
77 TStFlag = array[0..1023] of TStTagLevel;
78
79 TStOnRegExProgEvent = procedure(Sender : TObject; Percent : Word) of object;
80 TStOnMatchEvent = procedure(Sender : TObject;
81 REPosition : TMatchPosition) of object;
82
83
84 TStNodeHeap = class
85 private
86 FFreeList : PStPatRecord;
87
88 protected
89 procedure nhClearHeap;
90 function nhDeepCloneNode(aNode : PStPatRecord) : PStPatRecord;
91
92 public
93 constructor Create;
94 destructor Destroy; override;
95
96 function AllocNode : PStPatRecord;
97 procedure FreeNode(aNode : PStPatRecord);
98
99 function CloneNode(aNode : PStPatRecord) : PStPatRecord;
100 end;
101
102
103 TStStreamRegEx = class(TObject)
104 protected {private}
105 { Private declarations }
106 FAvoid : Boolean;
107 FIgnoreCase : Boolean;
108 FInTextStream : TStAnsiTextStream;
109 FInFileSize : Cardinal;
110 FInputStream : TStream;
111
112 FInLineBuf : PAnsiChar;
113 FInLineCount : Cardinal;
114 FInLineNum : Cardinal;
115 FInLineTermChar : AnsiChar;
116 FInLineTerminator : TStLineTerminator;
117 FInLineLength : integer;
118 FLineNumbers : Boolean;
119 FLinesPerSec : Cardinal;
120
121 FMatchCount : Cardinal;
122
123 FMatchPatSL : TStringList;
124 FMatchPatStr : PAnsiChar;
125 FMatchPatPtr : PStPatRecord;
126
127 FMaxLineLength : Cardinal;
128
129 FNodes : TStNodeHeap;
130
131 FOnMatch : TStOnMatchEvent;
132 FOutLineLength : integer;
133 FOutLineTermChar : AnsiChar;
134 FOutLineTerminator: TStLineTerminator;
135
136 FReplaceCount : Cardinal;
137 FReplacePatSL : TStringList;
138 FReplacePatStr : PAnsiChar;
139 FReplacePatPtr : PStPatRecord;
140
141 FOnProgress : TStOnRegExProgEvent;
142 FOutputStream : TStream;
143 FOutTextStream : TStAnsiTextStream;
144 FOutLineBuf : PAnsiChar;
145
146 FOutputOptions : TStOutputOptions;
147
148 FSelAvoidPatSL : TStringList;
149 FSelAvoidPatStr : PAnsiChar;
150 FSelAvoidPatPtr : PStPatRecord;
151
152 FSelectCount : Cardinal;
153
154 protected
155 { Protected declarations }
156
157 procedure AddTokenToPattern(var PatRec : PStPatRecord;
158 LastPatRec : PStPatRecord;
159 Token : TStTokens;
160 S : ShortString);
161 procedure AddTokenToReplace(var PatRec : PStPatRecord;
162 LastPatRec : PStPatRecord;
163 Token : TStTokens;
164 const S : ShortString); {!!.02}
165 function AppendS(Dest, S1, S2 : PAnsiChar; Count : Cardinal) : PAnsiChar;
166 function BuildAllPatterns : boolean;
167 function BuildPatternStr(var PStr : PAnsiChar;
168 var Len : Integer;
169 SL : TStringList) : Boolean;
170 function ConvertMaskToRegEx(const S : AnsiString) : AnsiString;
171 procedure DisposeItems(var Data : PStPatRecord);
172
173 procedure InsertLineNumber(Dest : PAnsiChar;
174 const S : PAnsiChar; LineNum : Integer);
175 function GetPattern(var Pattern : PAnsiChar;
176 var PatList : PStPatRecord) : Boolean;
177 function GetReplace(Pattern : PAnsiChar;
178 var PatList : PStPatRecord) : Boolean;
179 function MakePattern(var Pattern : PAnsiChar;
180 Start : Integer;
181 Delim : AnsiChar;
182 var TagOn : Boolean;
183 var PatList : PStPatRecord) : Integer;
184 function MakeReplacePattern(Pattern : PAnsiChar;
185 Start : Integer;
186 Delim : AnsiChar;
187 var PatList : PStPatRecord) : Integer;
188 function FindMatch(var Buf : PAnsiChar;
189 PatPtr : PStPatRecord;
190 var REPosition : TMatchPosition) : Boolean;
191 function MatchOnePatternElement(var Buf : PAnsiChar;
192 var I : Integer;
193 var TagOn : Boolean;
194 var TagNum : Integer;
195 PatPtr : PStPatRecord) : Boolean;
196 function ProcessLine(Buf : PAnsiChar;
197 Len : integer;
198 LineNum : integer;
199 CheckOnly : Boolean;
200 var REPosition: TMatchPosition) : Boolean;
201 function SearchMatchPattern(var Buf : PAnsiChar;
202 OffSet : Integer;
203 var TagOn : Boolean;
204 var TagNum : Integer;
205 PatPtr : PStPatRecord) : Integer;
206 procedure SetMatchPatSL(Value : TStringList);
207 procedure SetOptions(Value : TStOutputOptions);
208 procedure SetReplacePatSL(Value : TStringList);
209 procedure SetSelAvoidPatSL(Value : TStringList);
210 procedure SubLine(Buf : PAnsiChar);
211 function SubLineFindTag(Buf : PAnsiChar;
212 I : Integer;
213 IEnd : Integer;
214 TagNum : Integer;
215 var Flags : TStFlag;
216 var IStart : Integer;
217 var IStop : Integer) : Boolean;
218 function SubLineMatchOne(Buf : PAnsiChar;
219 var Flags : TStFlag;
220 var TagOn : Boolean;
221 var I : Integer;
222 var TagNum : Integer;
223 PatPtr : PStPatRecord) : Boolean;
224 function SubLineMatchPattern(Buf : PAnsiChar;
225 var Flags : TStFlag;
226 var TagOn : Boolean;
227 var TagNum : Integer;
228 OffSet : Integer;
229 PatPtr : PStPatRecord) : Integer;
230 procedure SubLineWrite(Buf : PAnsiChar;
231 S : PAnsiChar;
232 RepRec : PStPatRecord;
233 I,
234 IEnd : Integer;
235 var Flags : TStFlag);
236
237 public
238 { Public declarations }
239
240 property InputStream : TStream
241 read FInputStream
242 write FInputStream;
243
244 property OutputStream : TStream
245 read FOutputStream
246 write FOutputStream;
247
248 constructor Create;
249 destructor Destroy; override;
250
251 function CheckString(const S : AnsiString;
252 var REPosition : TMatchPosition) : Boolean;
253 function FileMasksToRegEx(Masks : AnsiString) : Boolean;
254 function Execute : Boolean;
255 function ReplaceString(var S : AnsiString;
256 var REPosition : TMatchPosition) : Boolean;
257
258 property Avoid : Boolean
259 read FAvoid
260 write FAvoid;
261
262 property IgnoreCase : Boolean
263 read FIgnoreCase
264 write FIgnoreCase;
265
266 property InFixedLineLength : integer
267 read FInLineLength
268 write FInLineLength;
269
270 property InLineTermChar : AnsiChar
271 read FInLineTermChar
272 write FInLineTermChar;
273
274 property InLineTerminator : TStLineTerminator
275 read FInLineTerminator
276 write FInLineTerminator;
277
278 property LineCount : Cardinal
279 read FInLineCount;
280
281 property LineNumbers : Boolean
282 read FLineNumbers
283 write FLineNumbers;
284
285 property LinesMatched : Cardinal
286 read FMatchCount;
287
288 property LinesPerSecond : Cardinal
289 read FLinesPerSec;
290
291 property LinesReplaced : Cardinal
292 read FReplaceCount;
293
294 property LinesSelected : Cardinal
295 read FSelectCount;
296
297 property MatchPattern : TStringList
298 read FMatchPatSL
299 write SetMatchPatSL;
300
301 property MaxLineLength : Cardinal
302 read FMaxLineLength
303 write FMaxLineLength;
304
305 property OnMatch : TStOnMatchEvent
306 read FOnMatch
307 write FOnMatch;
308
309 property OnProgress : TStOnRegExProgEvent
310 read FOnProgress
311 write FOnProgress;
312
313 property OutFixedLineLength : integer
314 read FOutLineLength
315 write FOutLineLength;
316
317 property OutLineTermChar : AnsiChar
318 read FOutLineTermChar
319 write FOutLineTermChar;
320
321 property OutLineTerminator : TStLineTerminator
322 read FOutLineTerminator
323 write FOutLineTerminator;
324
325 property OutputOptions : TStOutputOptions
326 read FOutputOptions
327 write SetOptions;
328
329 property ReplacePattern : TStringList
330 read FReplacePatSL
331 write SetReplacePatSL;
332
333 property SelAvoidPattern : TStringList
334 read FSelAvoidPatSL
335 write SetSelAvoidPatSL;
336 end;
337
338
339 TStRegEx = class(TStComponent)
340 protected {private}
341 FAvoid : Boolean;
342 FIgnoreCase : Boolean;
343 FInFileSize : Cardinal;
344 FInFileStream : TFileStream;
345 FInLineCount : Cardinal;
346
347 FInLineTermChar : AnsiChar;
348 FInLineTerminator : TStLineTerminator;
349 FInFixedLineLength: integer;
350 FInputFile : AnsiString;
351
352 FLineNumbers : Boolean;
353 FLinesPerSec : Cardinal;
354
355 FMatchCount : Cardinal;
356
357 FMatchPatSL : TStringList;
358 FMatchPatStr : PAnsiChar;
359 FMatchPatPtr : PStPatRecord;
360
361 FMaxLineLength : Cardinal;
362
363 FNodes : TStNodeHeap;
364
365 FOnProgress : TStOnRegExProgEvent;
366 FOnMatch : TStOnMatchEvent;
367
368 FOutFileStream : TFileStream;
369 FOutTextStream : TStAnsiTextStream;
370 FOutLineBuf : PAnsiChar;
371
372 FOutFixedLineLength : integer;
373 FOutLineTermChar : AnsiChar;
374 FOutLineTerminator: TStLineTerminator;
375
376 FOutputFile : AnsiString;
377 FOutputOptions : TStOutputOptions;
378
379 FReplaceCount : Cardinal;
380 FReplacePatSL : TStringList;
381 FReplacePatStr : PAnsiChar;
382 FReplacePatPtr : PStPatRecord;
383
384 FSelAvoidPatSL : TStringList;
385 FSelAvoidPatStr : PAnsiChar;
386 FSelAvoidPatPtr : PStPatRecord;
387
388 FSelectCount : Cardinal;
389
390 FStream : TStStreamRegEx;
391
392 protected
393 procedure SetMatchPatSL(Value : TStringList);
394 procedure SetOptions(Value : TStOutputOptions);
395 procedure SetReplacePatSL(Value : TStringList);
396 procedure SetSelAvoidPatSL(Value : TStringList);
397 procedure SetStreamProperties;
398 public
399 constructor Create(AOwner : TComponent); override;
400 destructor Destroy; override;
401
402 function CheckString(const S : AnsiString;
403 var REPosition : TMatchPosition) : Boolean;
404 function FileMasksToRegEx(const Masks : AnsiString) : Boolean; {!!.02}
405 function Execute : Boolean;
406 function ReplaceString(var S : AnsiString;
407 var REPosition : TMatchPosition) : Boolean;
408
409 property LineCount : Cardinal
410 read FInLineCount;
411
412 property LinesMatched : Cardinal
413 read FMatchCount;
414
415 property LinesPerSecond : Cardinal
416 read FLinesPerSec;
417
418 property LinesReplaced : Cardinal
419 read FReplaceCount;
420
421 property LinesSelected : Cardinal
422 read FSelectCount;
423
424 property MaxLineLength : Cardinal
425 read FMaxLineLength
426 write FMaxLineLength;
427
428 published
429 property Avoid : Boolean
430 read FAvoid
431 write FAvoid default False;
432
433 property IgnoreCase : Boolean
434 read FIgnoreCase
435 write FIgnoreCase default False;
436
437 property InFixedLineLength : Integer
438 read FInFixedLineLength
439 write FInFixedLineLength default 80;
440
441 property InLineTermChar : AnsiChar
442 read FInLineTermChar
443 write FInLineTermChar default #10;
444
445 property InLineTerminator : TStLineTerminator
446 read FInLineTerminator
447 write FInLineTerminator default ltCRLF;
448
449 property InputFile : AnsiString
450 read FInputFile
451 write FInputFile;
452
453 property LineNumbers : Boolean
454 read FLineNumbers
455 write FLineNumbers default False;
456
457 property MatchPattern : TStringList
458 read FMatchPatSL
459 write SetMatchPatSL;
460
461 property OnMatch : TStOnMatchEvent
462 read FOnMatch
463 write FOnMatch;
464
465 property OnProgress : TStOnRegExProgEvent
466 read FOnProgress
467 write FOnProgress;
468
469 property OutFixedLineLength : Integer
470 read FOutFixedLineLength
471 write FOutFixedLineLength default 80;
472
473 property OutLineTermChar : AnsiChar
474 read FOutLineTermChar
475 write FOutLineTermChar default #10;
476
477 property OutLineTerminator : TStLineTerminator
478 read FOutLineTerminator
479 write FOutLineTerminator default ltCRLF;
480
481 property OutputFile : AnsiString
482 read FOutputFile
483 write FOutputFile;
484
485 property OutputOptions : TStOutputOptions
486 read FOutputOptions
487 write SetOptions;
488
489 property ReplacePattern : TStringList
490 read FReplacePatSL
491 write SetReplacePatSL;
492
493 property SelAvoidPattern : TStringList
494 read FSelAvoidPatSL
495 write SetSelAvoidPatSL;
496 end;
497
498
499 implementation
500
501 uses
502 StStrL,
503 StStrS;
504
505
506 const
507 Null = #0;
508 EndStr = #0;
509 NewLine = #13#10;
510 Dash = '-';
511 Esc = '\';
512 Any = '.'; {was '?'}
513 Closure = '*';
514 ClosurePlus = '+';
515 MaybeOne = '?'; {was '!'}
516 Bol = '^';
517 Eol = '$';
518 Ccl = '[';
519 Negate = '^';
520 CclEnd = ']';
521 BTag = '{';
522 ETag = '}';
523 BGroup = '(';
524 EGroup = ')';
525 Alter = '|'; {was #}
526 Ditto = '&';
527 lSpace = 's';
528 lNewline = 'n';
529 lTab = 't';
530 lBackSpace = 'b';
531 lReturn = 'r';
532 lFeed = 'l';
533 lHex = 'h';
534 lWordDelim = 'w';
535 lNil = 'z';
536
537
538 function CleanUpCase(S : String) : String;
539 {-convert string to uppercase and remove duplicates}
540 var
541 I : Integer;
542 K : Cardinal;
543 C : Char;
544 begin
545 Result := '';
546 S := AnsiUpperCase(S);
547 for I := 1 to Length(S) do begin
548 C := S[I];
549 if not StrChPosL(Result, C, K) then
550 Result := Result + C;
551 end;
552 end;
553
554
555 procedure AppendChar(C : AnsiChar; var S : ShortString);
556 {-append a character C onto string S}
557 begin
558 S := S + C;
559 end;
560
561
562 function IsAlphaNum(C : AnsiChar) : Boolean;
563 begin
564 Result := IsCharAlphaNumericA(C); //Ansi!
565 end;
566
567
568 procedure ExpandDash(Delim : AnsiChar;
569 var Pattern : PAnsiChar ;
570 var I : Integer;
571 var S : ShortString);
572 {-expand the innards of the character class, including dashes}
573 {stop when endc is found}
574 {return a string S with the expansion}
575 var
576 C,
577 CLeft,
578 CNext : AnsiChar;
579 K : Integer;
580
581 begin
582 while (Pattern[I] <> Delim) and (Pattern[I] <> EndStr) do begin
583 C := Pattern[I];
584 if (C = Esc) then begin
585 if (Pattern[Succ(I)] <> EndStr) then begin
586 I := Succ(I);
587 C := Pattern[I];
588 case C of
589 lSpace : AppendChar(#32, S);
590 lTab : AppendChar(#9, S);
591 lBackSpace : AppendChar(#8, S);
592 lReturn : AppendChar(#13, S);
593 lFeed : AppendChar(#10, S);
594 else
595 AppendChar(C, S);
596 end;
597 end else
598 {escape must be the character}
599 AppendChar(Esc, S);
600 end else if (C <> Dash) then
601 {literal character}
602 AppendChar(C, S)
603 else if ((Length(S) = 0) or (Pattern[Succ(I)] = Delim)) then
604 {literal dash at begin or end of class}
605 AppendChar(Dash, S)
606 else begin
607 {dash in middle of class}
608 CLeft := Pattern[Pred(I)];
609 CNext := Pattern[Succ(I)];
610 if IsAlphaNum(CLeft) and IsAlphaNum(CNext) and (CLeft <= CNext) then begin
611 {legal dash to be expanded}
612 for K := (Ord(CLeft)+1) to Ord(CNext) do
613 AppendChar(AnsiChar(K), S);
614 {move over the end of dash character}
615 I := Succ(I);
616 end else
617 {dash must be a literal}
618 AppendChar(Dash, S);
619 end;
620 I := Succ(I);
621 end;
622 end;
623
624
625 function GetCharacterClass(var Pattern : PAnsiChar;
626 var I : Integer;
627 var S : ShortString;
628 var AToken : TStTokens) : Boolean;
629 {-expand a character class starting at position I of Pattern into a string S}
630 {return a token type (tknCharClass or tknNegCharClass)}
631 {return I pointing at the end of class character}
632 {return true if successful}
633
634 begin
635 {skip over start of class character}
636 I := Succ(I);
637 if (Pattern[I] = Negate) then begin
638 AToken := tknNegCharClass;
639 I := Succ(I);
640 end else
641 AToken := tknCharClass;
642 {expand the character class}
643 S := '';
644 ExpandDash(CclEnd, Pattern, I, S);
645 Result := (Pattern[I] = CclEnd);
646 end;
647
648
649
650
651
652 {******************************************************************************}
653 { TStNodeHeap Implementation }
654 {******************************************************************************}
655
656 constructor TStNodeHeap.Create;
657 begin
658 inherited Create;
659
660 New(FFreeList);
661 FillChar(FFreeList^, sizeof(TStPatRecord), 0);
662 end;
663
664
665 destructor TStNodeHeap.Destroy;
666 begin
667 nhClearHeap;
668 Dispose(FFreeList);
669
670 inherited Destroy;
671 end;
672
673
674 function TStNodeHeap.AllocNode : PStPatRecord;
675 begin
676 if (FFreeList^.NextPattern = nil) then
677 New(Result)
678 else begin
679 Result := FFreeList^.NextPattern;
680 FFreeList^.NextPattern := Result^.NextPattern;
681 end;
682 FillChar(Result^, sizeof(TStPatRecord), 0);
683 end;
684
685
686 function TStNodeHeap.CloneNode(aNode : PStPatRecord) : PStPatRecord;
687 begin
688 {allocate a new node}
689 Result := AllocNode;
690
691 {copy fields}
692 Result^.Token := aNode^.Token;
693 Result^.OneChar := aNode^.OneChar;
694 Result^.NextOK := aNode^.NextOK;
695 if (aNode^.StrPtr <> nil) then begin
696 New(Result^.StrPtr);
697 Result^.StrPtr^ := aNode^.StrPtr^;
698 end else
699 Result^.StrPtr := nil;
700
701 {deep clone the nested node}
702 if (aNode^.NestedPattern <> nil) then
703 Result^.NestedPattern := nhDeepCloneNode(aNode^.NestedPattern);
704 end;
705
706
707 procedure TStNodeHeap.FreeNode(aNode : PStPatRecord);
708 begin
709 if (aNode <> nil) then begin
710 aNode^.NextPattern := FFreeList^.NextPattern;
711 FFreeList^.NextPattern := aNode;
712 end;
713 end;
714
715
716 procedure TStNodeHeap.nhClearHeap;
717 var
718 Walker,
719 Temp : PStPatRecord;
720 begin
721 Walker := FFreeList^.NextPattern;
722 FFreeList^.NextPattern := nil;
723 while (Walker <> nil) do begin
724 Temp := Walker;
725 Walker := Walker^.NextPattern;
726 Dispose(Temp);
727 end;
728 end;
729
730
731 function TStNodeHeap.nhDeepCloneNode(aNode : PStPatRecord) : PStPatRecord;
732 begin
733 {allocate a new node}
734 Result := AllocNode;
735
736 {copy fields}
737 Result^.Token := aNode^.Token;
738 Result^.OneChar := aNode^.OneChar;
739 Result^.NextOK := aNode^.NextOK;
740 if (aNode^.StrPtr <> nil) then begin
741 New(Result^.StrPtr);
742 Result^.StrPtr^ := aNode^.StrPtr^;
743 end else
744 Result^.StrPtr := nil;
745
746 {recursively deepclone the next and nested nodes}
747 if (aNode^.NextPattern <> nil) then
748 Result^.NextPattern := nhDeepCloneNode(aNode^.NextPattern);
749 if (aNode^.NestedPattern <> nil) then
750 Result^.NestedPattern := nhDeepCloneNode(aNode^.NestedPattern);
751 end;
752
753
754 {******************************************************************************}
755 { TStStreamRegEx Implementation }
756 {******************************************************************************}
757
758
759 constructor TStStreamRegEx.Create;
760 begin
761 inherited Create;
762
763 FAvoid := False;
764 FIgnoreCase := False;
765 FLineNumbers := False;
766 FOutputOptions := [];
767
768 FInLineTerminator := ltCRLF;
769 FInLineTermChar := #10;
770 FInLineLength := 80;
771
772 FOutLineTerminator := ltCRLF;
773 FOutLineTermChar := #10;
774 FOutLineLength := 80;
775
776 FMaxLineLength := 1024;
777
778 FMatchPatSL := TStringList.Create;
779 FMatchPatPtr := nil;
780 FSelAvoidPatSL := TStringList.Create;
781 FSelAvoidPatPtr:= nil;
782 FReplacePatSL := TStringList.Create;
783 FReplacePatPtr := nil;
784
785 FInputStream := nil;
786 FInTextStream := nil;
787 FOutputStream := nil;
788 FOutTextStream := nil;
789
790 FNodes := TStNodeHeap.Create;
791 end;
792
793
794 procedure TStStreamRegEx.DisposeItems(var Data : PStPatRecord);
795 var
796 Walker, Temp : PStPatRecord;
797 begin
798 if (Data <> nil) then begin
799 Walker := Data;
800 while (Walker <> nil) do begin
801 Temp := Walker;
802
803 if (Assigned(Walker^.StrPtr)) then
804 Dispose(Walker^.StrPtr);
805
806 if (Assigned(Walker^.NestedPattern)) then
807 DisposeItems(Walker^.NestedPattern);
808
809 Walker := Walker^.NextPattern;
810 FNodes.FreeNode(Temp);
811 end;
812 Data := nil;
813 end;
814 end;
815
816
817 destructor TStStreamRegEx.Destroy;
818 begin
819 DisposeItems(FMatchPatPtr);
820 DisposeItems(FSelAvoidPatPtr);
821 DisposeItems(FReplacePatPtr);
822
823 FNodes.Free;
824 FNodes := nil;
825
826 if (Assigned(FMatchPatStr)) then begin
827 FreeMem(FMatchPatStr, StrLen(FMatchPatStr) + 1);
828 FMatchPatStr := nil;
829 end;
830
831 if (Assigned(FReplacePatStr)) then
832 FreeMem(FReplacePatStr, StrLen(FReplacePatStr) + 1);
833 FReplacePatStr := nil;
834
835 if (Assigned(FSelAvoidPatStr)) then
836 FreeMem(FSelAvoidPatStr, StrLen(FSelAvoidPatStr) + 1);
837 FSelAvoidPatStr := nil;
838
839 FMatchPatSL.Free;
840 FMatchPatSL := nil;
841
842 FReplacePatSL.Free;
843 FReplacePatSL := nil;
844
845 FSelAvoidPatSL.Free;
846 FSelAvoidPatSL := nil;
847
848 inherited Destroy;
849 end;
850
851
852 function TStStreamRegEx.AppendS(Dest, S1, S2 : PAnsiChar;
853 Count : Cardinal) : PAnsiChar;
854 var
855 Remaining : Cardinal;
856 I : Cardinal;
857 begin
858 Result := Dest;
859 I := StrLen(S1);
860 Remaining := MaxLineLength - I;
861 if (Remaining < StrLen(S2)) then
862 Count := Remaining;
863 Move(S1[0], Dest[0], I);
864 Move(S2[0], Dest[I], Count);
865 I := I + Count;
866 Dest[I] := #0;
867 end;
868
869
870 function TStStreamRegEx.BuildAllPatterns : Boolean;
871 var
872 Len : Integer;
873 begin
874 if (FMatchPatSL.Count > 0) then begin
875 DisposeItems(FMatchPatPtr);
876
877 if (BuildPatternStr(FMatchPatStr, Len, FMatchPatSL)) then begin
878 if (Len > 0) then
879 GetPattern(FMatchPatStr, FMatchPatPtr)
880 else
881 DisposeItems(FMatchPatPtr);
882 Result := True;
883 end else begin
884 DisposeItems(FMatchPatPtr);
885 Result := False;
886 end;
887 end else begin
888 DisposeItems(FMatchPatPtr);
889 Result := True;
890 end;
891
892 if Result then begin
893 if (FSelAvoidPatSL.Count > 0) then begin
894 DisposeItems(FSelAvoidPatPtr);
895 if (BuildPatternStr(FSelAvoidPatStr, Len, FSelAvoidPatSL)) then begin
896 if (Len > 0) then
897 GetPattern(FSelAvoidPatStr, FSelAvoidPatPtr)
898 else
899 DisposeItems(FSelAvoidPatPtr);
900 Result := True;
901 end else begin
902 DisposeItems(FSelAvoidPatPtr);
903 Result := False;
904 end;
905 end else begin
906 DisposeItems(FSelAvoidPatPtr);
907 Result := True;
908 end;
909 end;
910
911 if Result then begin
912 if (FReplacePatSL.Count > 0) then begin
913 DisposeItems(FReplacePatPtr);
914 if (BuildPatternStr(FReplacePatStr, Len, FReplacePatSL)) then begin
915 if (Len > 0) then
916 GetReplace(FReplacePatStr, FReplacePatPtr)
917 else
918 DisposeItems(FReplacePatPtr);
919 Result := True;
920 end else begin
921 DisposeItems(FReplacePatPtr);
922 Result := False;
923 end;
924 end else begin
925 DisposeItems(FReplacePatPtr);
926 Result := True;
927 end;
928 end;
929 end;
930
931
932
933 function TStStreamRegEx.BuildPatternStr(var PStr : PAnsiChar;
934 var Len : Integer;
935 SL : TStringList) : Boolean;
936 var
937 I,
938 J : integer;
939 CurLen : Integer; {!!.01}
940 begin
941 Len := 0;
942 for I := 0 to pred(SL.Count) do
943 Len := Len + Length(TrimL(SL[I]));
944 if (Len = 0) then
945 Result := True
946 else begin
947 if Assigned(PStr) then
948 FreeMem(PStr, StrLen(PStr)+1);
949 GetMem(PStr, Len+1);
950 PStr[Len] := EndStr;
951 J := 0;
952 for I := 0 to pred(SL.Count) do begin
953 CurLen := Length(TrimL(SL[I])); {!!.01}
954 if CurLen > 0 then begin {!!.01}
955 Move(SL[I][1], PStr[J], CurLen); {!!.01}
956 Inc(J, CurLen); {!!.01}
957 end; {!!.01}
958 end;
959 Result := True;
960 end;
961 end;
962
963
964 function TStStreamRegEx.CheckString(const S : AnsiString;
965 var REPosition : TMatchPosition) : Boolean;
966 var
967 Tmp : PAnsiChar;
968 I : integer;
969 Len : integer;
970 OK : Boolean;
971 begin
972 I := Length(S);
973 GetMem(Tmp, I+3);
974 try
975 if I > 0 then {!!.01}
976 Move(S[1], Tmp[0], I);
977
978 Tmp[I] := #13;
979 Tmp[I+1] := #10;
980 Tmp[I+2] := EndStr;
981
982 if (FMatchPatSL.Count > 0) then begin
983 OK := BuildPatternStr(FMatchPatStr, Len, FMatchPatSL);
984 if (OK) then begin
985 if (Len > 0) then
986 GetPattern(FMatchPatStr, FMatchPatPtr)
987 else
988 DisposeItems(FMatchPatPtr);
989 end else
990 DisposeItems(FMatchPatPtr);
991 end else
992 DisposeItems(FMatchPatPtr);
993
994 if (FSelAvoidPatSL.Count > 0) then begin
995 OK := BuildPatternStr(FSelAvoidPatStr, Len, FSelAvoidPatSL);
996 if (OK) then begin
997 if (Len > 0) then
998 GetPattern(FSelAvoidPatStr, FSelAvoidPatPtr)
999 else
1000 DisposeItems(FSelAvoidPatPtr);
1001 end;
1002 end else
1003 DisposeItems(FSelAvoidPatPtr);
1004
1005 FMatchCount := 0;
1006 FSelectCount := 0;
1007 FReplaceCount := 0;
1008 FInLineCount := 0;
1009 FLinesPerSec := 0;
1010
1011 REPosition.LineNum := 1;
1012 if ((FSelAvoidPatPtr <> nil) or (FMatchPatPtr <> nil)) then
1013 Result := ProcessLine(Tmp, I, 1, True, REPosition)
1014 else begin
1015 Result := False;
1016 RaiseStError(EStRegExError, stscNoPatterns);
1017 end;
1018 finally
1019 FreeMem(Tmp, I+3);
1020 end;
1021 end;
1022
1023
1024 function TStStreamRegEx.ReplaceString(var S : AnsiString;
1025 var REPosition : TMatchPosition) : Boolean;
1026 var
1027 Tmp : PAnsiChar;
1028 I : integer;
1029 Len : integer;
1030 OK : Boolean;
1031
1032 function ProcessString(var S : AnsiString;
1033 Len : integer;
1034 LineNum : integer;
1035 var REPosition : TMatchPosition) : Boolean;
1036 var
1037 TmpBuf : PAnsiChar;
1038 ABuf : PAnsiChar;
1039 L : Integer;
1040 begin
1041 L := Length(S)+1;
1042 GetMem(TmpBuf, MaxLineLength+1);
1043 GetMem(ABuf, L);
1044 try
1045 StrPCopy(ABuf, S);
1046 if (FSelAvoidPatPtr <> nil) then begin
1047 Result := False;
1048 if (not Avoid) then
1049 Result := FindMatch(ABuf, FSelAvoidPatPtr, REPosition)
1050 else
1051 Result := not(FindMatch(ABuf, FSelAvoidPatPtr, REPosition));
1052 end else
1053 Result := True;
1054
1055 if Result then begin
1056 {met select criterion, perhaps by default}
1057 FSelectCount := Succ(FSelectCount);
1058 if (FReplacePatPtr <> nil) then begin
1059 Result := FindMatch(ABuf, FMatchPatPtr, REPosition);
1060 if Result then begin
1061 TmpBuf[0] := #0;
1062 SubLine(ABuf);
1063 S := StrPas(FOutLineBuf);
1064 end;
1065 end;
1066 end;
1067 finally
1068 FreeMem(TmpBuf, MaxLineLength+1);
1069 FreeMem(ABuf, L);
1070 end;
1071 end;
1072
1073
1074 begin
1075 I := Length(S);
1076 GetMem(Tmp, I+3);
1077 try
1078 if I > 0 then {!!.01}
1079 Move(S[1], Tmp[0], I);
1080 Tmp[I] := #13;
1081 Tmp[I+1] := #10;
1082 Tmp[I+2] := EndStr;
1083
1084 if (FMatchPatSL.Count > 0) then begin
1085 OK := BuildPatternStr(FMatchPatStr, Len, FMatchPatSL);
1086 if (OK) then begin
1087 if (Len > 0) then
1088 GetPattern(FMatchPatStr, FMatchPatPtr)
1089 else
1090 DisposeItems(FMatchPatPtr);
1091 end else
1092 DisposeItems(FMatchPatPtr);
1093 end else
1094 DisposeItems(FMatchPatPtr);
1095
1096 if (FSelAvoidPatSL.Count > 0) then begin
1097 OK := BuildPatternStr(FSelAvoidPatStr, Len, FSelAvoidPatSL);
1098 if (OK) then begin
1099 if (Len > 0) then
1100 GetPattern(FSelAvoidPatStr, FSelAvoidPatPtr)
1101 else
1102 DisposeItems(FSelAvoidPatPtr);
1103 end;
1104 end else
1105 DisposeItems(FSelAvoidPatPtr);
1106
1107 if (FReplacePatSL.Count > 0) then begin
1108 OK := BuildPatternStr(FReplacePatStr, Len, FReplacePatSL);
1109 if (OK) then begin
1110 if (Len > 0) then
1111 GetPattern(FReplacePatStr, FReplacePatPtr)
1112 else
1113 DisposeItems(FReplacePatPtr);
1114 end else
1115 DisposeItems(FReplacePatPtr);
1116 end else
1117 DisposeItems(FReplacePatPtr);
1118
1119 FMatchCount := 0;
1120 FSelectCount := 0;
1121 FReplaceCount := 0;
1122 FInLineCount := 0;
1123 FLinesPerSec := 0;
1124
1125 GetMem(FInLineBuf, MaxLineLength+3);
1126 GetMem(FOutLineBuf, MaxLineLength+3);
1127 try
1128 REPosition.LineNum := 1;
1129 if ((FSelAvoidPatPtr <> nil) or (FMatchPatPtr <> nil)) and
1130 (Assigned(FReplacePatPtr))then begin
1131 Result := ProcessString(S, I, 1, REPosition);
1132 end else begin
1133 Result := False;
1134 RaiseStError(EStRegExError, stscNoPatterns);
1135 end;
1136 finally
1137 FreeMem(FInLineBuf, MaxLineLength+3);
1138 FreeMem(FOutLineBuf, MaxLineLength+3);
1139 end;
1140 finally
1141 FreeMem(Tmp, I+3);
1142 end;
1143 end;
1144
1145
1146 function TStStreamRegEx.ConvertMaskToRegEx(const S : AnsiString) : AnsiString;
1147 var
1148 I : integer;
1149 TS : AnsiString;
1150 begin
1151 I := 1;
1152 while (I <= Length(S)) do begin
1153 if (I = 1) then begin
1154 if not (S[1] in ['*', '?']) then begin
1155 TS := '((^[' ;
1156 TS := TS + S[1] + '])';
1157 Inc(I);
1158 end else
1159 TS := '(';
1160 end;
1161
1162 if not (S[I] in ['*', '?', '.', '\']) then
1163 TS := TS + S[I]
1164 else begin
1165 if (S[I] = '*') then
1166 TS := TS + '.*'
1167 else if (S[I] = '?') then begin
1168 if (I = 1) then
1169 TS := TS + '(^.)'
1170 else
1171 TS := TS + '.?';
1172 end else begin
1173 TS := TS + '\' + S[I];
1174 end;
1175 end;
1176 Inc(I);
1177 end;
1178 Result := TS + '\n)';
1179 end;
1180
1181
1182 function TStStreamRegEx.FileMasksToRegEx(Masks : AnsiString) : Boolean;
1183 var
1184 SL : TStringList;
1185 S : AnsiString;
1186 K : Cardinal;
1187 Len: Integer;
1188 begin
1189 SL := TStringList.Create;
1190 try
1191 if StrChPosL(Masks, ';', K) then begin
1192 while (K > 0) do begin
1193 S := Copy(Masks, 1, K-1);
1194 if (Length(S) > 0) then begin
1195 if (SL.Count = 0) then
1196 SL.Add(ConvertMaskToRegEx(S))
1197 else
1198 SL.Add('|' + ConvertMaskToRegEx(S));
1199 end;
1200 Delete(Masks, 1, K);
1201 if not (StrChPosL(Masks, ';', K)) then
1202 break;
1203 end;
1204 if (Length(Masks) > 0) then
1205 SL.Add('|' + ConvertMaskToRegEx(Masks));
1206 end else begin
1207 if (Length(Masks) > 0) then
1208 SL.Add(ConvertMaskToRegEx(Masks));
1209 end;
1210
1211 if (SL.Count > 0) then begin
1212 FMatchPatSL.Clear;
1213 FMatchPatSL.Assign(SL);
1214 DisposeItems(FMatchPatPtr);
1215 FMatchPatPtr := nil;
1216 if (BuildPatternStr(FMatchPatStr, Len, FMatchPatSL)) then begin
1217 if (Len > 0) then
1218 GetPattern(FMatchPatStr, FMatchPatPtr)
1219 else begin
1220 DisposeItems(FMatchPatPtr);
1221 FMatchPatPtr := nil;
1222 end;
1223 Result := True;
1224 end else begin
1225 DisposeItems(FMatchPatPtr);
1226 FMatchPatPtr := nil;
1227 Result := False;
1228 end;
1229 Result := True;
1230 end else
1231 Result := False;
1232 finally
1233 SL.Free;
1234 end;
1235 end;
1236
1237
1238
1239 function TStStreamRegEx.Execute : Boolean;
1240 var
1241 Len : TStMemSize;
1242 LineNum : Integer;
1243 ATime : TDateTime;
1244 PC : Cardinal;
1245 LPC : Cardinal;
1246 BytesRead : Cardinal;
1247 REPosition: TMatchPosition;
1248 Found : Boolean;
1249
1250 Src : PAnsiChar; {!!!}
1251 FFoundText : AnsiString; {!!!}
1252 begin
1253 if (FMatchPatSL.Count = 0) and
1254 (FReplacePatSL.Count = 0) and (FSelAvoidPatSL.Count = 0) then
1255 RaiseStError(EStRegExError, stscNoPatterns);
1256
1257 if (not (BuildAllPatterns)) then
1258 RaiseStError(EStRegExError, stscPatternError);
1259
1260 if (FMatchPatPtr = nil) and (FSelAvoidPatPtr = nil) and (FReplacePatPtr = nil) then
1261 RaiseStError(EStRegExError, stscNoPatterns);
1262
1263 if (not (Assigned(FInputStream))) or
1264 ((not (Assigned(FOutputStream)) and (not (ooCountOnly in OutputOptions)))) then
1265 RaiseStError(EStRegExError, stscStreamsNil);
1266
1267 FInTextStream := nil;
1268 FOutTextStream := nil;
1269 try
1270 FInTextStream := TStAnsiTextStream.Create(FInputStream);
1271 FInTextStream.LineTermChar := FInLineTermChar;
1272 FInTextStream.LineTerminator := FInLineTerminator;
1273 FInTextStream.FixedLineLength := FInLineLength;
1274 FInFileSize := FInTextStream.Size;
1275
1276 if not (ooCountOnly in OutputOptions) then begin
1277 FOutTextStream := TStAnsiTextStream.Create(FOutputStream);
1278 FOutTextStream.LineTermChar := FOutLineTermChar;
1279 FOutTextStream.LineTerminator := FOutLineTerminator;
1280 FOutTextStream.FixedLineLength := FInLineLength;
1281 end;
1282
1283 FMatchCount := 0;
1284 FSelectCount := 0;
1285 FReplaceCount := 0;
1286 FInLineCount := 0;
1287 FLinesPerSec := 0;
1288 BytesRead := 0;
1289 LPC := 0;
1290
1291 FInTextStream.Position := 0;
1292 FInLineBuf := nil;
1293 FOutLineBuf := nil;
1294 try
1295 GetMem(FInLineBuf, MaxLineLength+3);
1296 GetMem(FOutLineBuf, MaxLineLength+3);
1297
1298 LineNum := 1;
1299 ATime := Now;
1300 while not FInTextStream.AtEndOfStream do begin
1301 Len := FInTextStream.ReadLineArray(FInLineBuf, MaxLineLength);
1302 Inc(BytesRead, Len);
1303
1304 FInLineBuf[Len] := #13;
1305 FInLineBuf[Len+1] := #10;
1306 FInLineBuf[Len+2] := EndStr;
1307 {!!.02 - added }
1308 REPosition.StartPos := 0;
1309 REPosition.EndPos := 0;
1310 REPosition.Length := 0;
1311 {!!.02 - added end }
1312 REPosition.LineNum := LineNum;
1313 Found := ProcessLine(FInLineBuf, Len, LineNum, False, REPosition);
1314
1315 {!!!}
1316 SetLength(FFoundText, REPosition.Length);
1317 Src := FInLineBuf;
1318 Inc(Src, REPosition.StartPos);
1319 StrMove(PAnsiChar(FFoundText), Src, REPosition.Length);
1320 {!!!}
1321
1322 if (FInFileSize > 0) then begin
1323 PC := Round(BytesRead / FInFileSize * 100);
1324 {avoid calling with every line - when OnProgress is assigned}
1325 {performance is considerably reduced anyway, don't add to it}
1326 if (PC > LPC) then begin
1327 LPC := PC;
1328 if (Assigned(FOnProgress)) then
1329 FOnProgress(Self, PC);
1330 end;
1331 end;
1332 if (Assigned(FOnMatch)) and (Found) then
1333 FOnMatch(Self, REPosition);
1334
1335 Inc(LineNum);
1336 end;
1337 ATime := (Now - ATime) * 86400;
1338 FInLineCount := LineNum-1;
1339 if (ATime > 0) then
1340 FLinesPerSec := Trunc(FInLineCount / ATime)
1341 else
1342 FLinesPerSec := 0;
1343 if (Assigned(FOnProgress)) then
1344 FOnProgress(Self, 100);
1345 Result := (FMatchCount > 0) or (FSelectCount > 0);
1346 finally
1347 FreeMem(FInLineBuf, MaxLineLength+3);
1348 FreeMem(FOutLineBuf, MaxLineLength+3);
1349 end;
1350 finally
1351 FInTextStream.Free;
1352 FInTextStream := nil;
1353 FOutTextStream.Free;
1354 FOutTextStream := nil;
1355 end;
1356 end;
1357
1358
1359 procedure TStStreamRegEx.AddTokenToPattern(var PatRec : PStPatRecord;
1360 LastPatRec : PStPatRecord;
1361 Token : TStTokens;
1362 S : ShortString);
1363 {-add a token record to the pattern list}
1364 {-S contains a literal character or an expanded character class}
1365
1366
1367 begin
1368 PatRec := FNodes.AllocNode;
1369 PatRec^.Token := Token; {save token type}
1370 PatRec^.NextOK := False; {default to non-alternation}
1371
1372 LastPatRec^.NextPattern := PatRec; {hook up the previous token}
1373 case Token of
1374 tknNil, tknAnyChar, tknBegOfLine, tknEndOfLine, tknGroup, tknBegTag, tknEndTag :
1375 begin
1376 PatRec^.OneChar := Null;
1377 PatRec^.StrPtr := nil;
1378 end;
1379 tknLitChar :
1380 begin
1381 if IgnoreCase then
1382 PatRec^.OneChar := AnsiChar(AnsiUpperCase(S[1])[1])
1383 else
1384 PatRec^.OneChar := S[1];
1385 PatRec^.StrPtr := nil;
1386 end;
1387 tknCharClass, tknNegCharClass :
1388 begin
1389 PatRec^.OneChar := Null;
1390 if FIgnoreCase then
1391 S := CleanUpCase(S);
1392 New(PatRec^.StrPtr);
1393 PatRec^.StrPtr^ := S;
1394 end;
1395 else
1396 RaiseStError(EStRegExError, stscUnknownError);
1397 end;
1398 end;
1399
1400
1401 function TStStreamRegEx.MakePattern(var Pattern : PAnsiChar;
1402 Start : Integer;
1403 Delim : AnsiChar;
1404 var TagOn : Boolean;
1405 var PatList : PStPatRecord) : Integer;
1406 var
1407 I : Integer;
1408 NextLastPatRec,
1409 LastPatRec,
1410 TempPatRec,
1411 PatRec : PStPatRecord;
1412 Done : Boolean;
1413 AChar : AnsiChar;
1414 TmpStr : ShortString;
1415 AToken : TStTokens;
1416 GroupStartPos,
1417 GroupEndPos : integer;
1418
1419 begin
1420 PatList := FNodes.AllocNode;
1421 PatList^.Token := tknNil; {put a nil token at the beginning}
1422 PatList^.NextOK := False;
1423 LastPatRec := PatList;
1424 NextLastPatRec := nil;
1425
1426 I := Start; {start point of pattern string}
1427 Done := False;
1428 while not(Done) and (Pattern[I] <> Delim) and (Pattern[I] <> EndStr) do begin
1429 AChar := Pattern[I];
1430 if (AChar = Any) then
1431 AddTokenToPattern(PatRec, LastPatRec, tknAnyChar, AChar)
1432 else if (AChar = Bol) then
1433 AddTokenToPattern(PatRec, LastPatRec, tknBegOfLine, '')
1434 else if (AChar = Eol) then
1435 AddTokenToPattern(PatRec, LastPatRec, tknEndOfLine, '')
1436 else if (AChar = Ccl) then begin
1437 Done := (GetCharacterClass(Pattern, I, TmpStr, AToken) = False);
1438 if Done then
1439 RaiseStError(EStRegExError, stscExpandingClass);
1440 AddTokenToPattern(PatRec, LastPatRec, AToken, TmpStr);
1441 end else if (AChar = Alter) then begin
1442 if (NextLastPatRec = nil) or
1443 ((NextLastPatRec^.Token <> tknClosure) and
1444 (NextLastPatRec^.Token <> tknMaybeOne)) then begin
1445 {flag the current token as non-critical, i.e., "next is OK"}
1446 LastPatRec^.NextOK := True;
1447 end else begin
1448 {alternation immediately after a closure is probably not desired}
1449 {e.g., [a-z]*|[0-9] would internally produce ([a-z]|[0-9])*}
1450 Done := True;
1451 RaiseStError(EStRegExError, stscAlternationFollowsClosure);
1452 end;
1453 end else if (AChar = BGroup) then begin
1454 GroupStartPos := I+1;
1455 AddTokenToPattern(PatRec, LastPatRec, tknGroup, '');
1456 {recursive branch off the list}
1457 I := MakePattern(Pattern, Succ(I), EGroup, TagOn, TempPatRec);
1458 if (I > 0) then begin
1459 GroupEndPos := I-1;
1460 if (Pattern[I+1] <> EndStr) then begin
1461 if (Pattern[I+1] in [Closure, ClosurePlus]) then begin
1462 if ((((GroupEndPos - GroupStartPos) = 1) or
1463 (((GroupEndPos - GroupStartPos) = 2) and (Pattern[GroupStartPos] = Esc))) and
1464 (Pattern[GroupEndPos] in [Closure, MaybeOne])) then begin
1465 Done := True;
1466 RaiseStError(EStRegExError, stscClosureMaybeEmpty);
1467 end else
1468 PatRec^.NestedPattern := TempPatRec;
1469 end else
1470 PatRec^.NestedPattern := TempPatRec;
1471 end else
1472 PatRec^.NestedPattern := TempPatRec;
1473 end else begin
1474 {didn't find egroup}
1475 Done := True;
1476 RaiseStError(EStRegExError, stscUnbalancedParens);
1477 end;
1478 end else if ((AChar = BTag) and (not(TagOn))) then begin
1479 AddTokenToPattern(PatRec, LastPatRec, tknBegTag, '');
1480 TagOn := True;
1481 end else if ((AChar = ETag) and (TagOn)) then begin
1482 AddTokenToPattern(PatRec, LastPatRec, tknEndTag, '');
1483 TagOn := False;
1484 end else if (((AChar = Closure) or (AChar = ClosurePlus) or
1485 (AChar = MaybeOne)) and (I > Start)) then begin
1486 if ((LastPatRec^.Token in [tknBegOfLine, tknEndOfLine, tknMaybeOne, tknClosure]) or
1487 (NextLastPatRec^.Token = tknClosure)) then begin
1488 {error, can't have closure after any of these}
1489 Done := True;
1490 RaiseStError(EStRegExError, stscFollowingClosure);
1491 end else begin
1492 if (AChar = ClosurePlus) then begin
1493 {insert an extra copy of the last token before the closure}
1494 TempPatRec := FNodes.CloneNode(LastPatRec);
1495 NextLastPatRec^.NextPattern := TempPatRec;
1496 TempPatRec^.NextPattern := LastPatRec;
1497 NextLastPatRec := TempPatRec;
1498 end;
1499 {insert the closure between next to last and last token}
1500 TempPatRec := FNodes.AllocNode;
1501 NextLastPatRec^.NextPattern := TempPatRec;
1502 if (AChar = MaybeOne) then
1503 TempPatRec^.Token := tknMaybeOne
1504 else
1505 TempPatRec^.Token := tknClosure;
1506 TempPatRec^.OneChar := Null;
1507
1508 TempPatRec^.NextPattern := LastPatRec;
1509 TempPatRec^.NextOK := False;
1510 {set j and lastj back into sequence}
1511 PatRec := LastPatRec;
1512 LastPatRec := TempPatRec;
1513 end;
1514 end else begin
1515 if (AChar = Esc) then begin
1516 {skip over escape character}
1517 I := Succ(I);
1518 AChar := Pattern[I];
1519 case AChar of
1520 lSpace : AddTokenToPattern(PatRec, LastPatRec, tknLitChar, #32);
1521 lNewline :
1522 begin
1523 AddTokenToPattern(PatRec, LastPatRec, tknLitChar, #13);
1524 LastPatRec := PatRec;
1525 AddTokenToPattern(PatRec, LastPatRec, tknLitChar, #10);
1526 end;
1527 lTab : AddTokenToPattern(PatRec, LastPatRec, tknLitChar, #9);
1528 lBackSpace : AddTokenToPattern(PatRec, LastPatRec, tknLitChar, #8);
1529 lReturn : AddTokenToPattern(PatRec, LastPatRec, tknLitChar, #13);
1530 lFeed : AddTokenToPattern(PatRec, LastPatRec, tknLitChar, #10);
1531 lWordDelim : AddTokenToPattern(PatRec, LastPatRec, tknCharClass, StWordDelimString);
1532 lHex : AddTokenToPattern(PatRec, LastPatRec, tknCharClass, StHexDigitString);
1533 else
1534 AddTokenToPattern(PatRec, LastPatRec, tknLitChar,AChar);
1535 end;
1536 end else
1537 AddTokenToPattern(PatRec, LastPatRec, tknLitChar, AChar);
1538 end;
1539 NextLastPatRec := LastPatRec;
1540 LastPatRec := PatRec;
1541 if not(Done) then
1542 I := Succ(I);
1543 end; {of looking through pattern string}
1544
1545 if ((Done) or (Pattern[I] <> Delim)) then begin
1546 Result := 0;
1547 RaiseStError(EStRegExError, stscPatternError);
1548 end else
1549 Result := I;
1550 end;
1551
1552
1553 function TStStreamRegEx.GetPattern(var Pattern : PAnsiChar;
1554 var PatList : PStPatRecord) : Boolean;
1555 {-convert a Pattern PAnsiChar into a pattern list, pointed to by patlist}
1556 {-return true if successful}
1557 var
1558 TagOn : Boolean;
1559 begin
1560 TagOn := False;
1561 Result := (MakePattern(Pattern, 0, EndStr, TagOn, PatList) > 0);
1562 if TagOn then begin
1563 GetPattern := False;
1564 RaiseStError(EStRegExError, stscUnbalancedTag);
1565 end;
1566 end;
1567
1568
1569 procedure TStStreamRegEx.AddTokenToReplace(var PatRec : PStPatRecord;
1570 LastPatRec : PStPatRecord;
1571 Token : TStTokens;
1572 const S : ShortString); {!!.02}
1573 {-add a token record to the pattern list}
1574 {S contains a literal character or an expanded character class}
1575 begin
1576 PatRec := FNodes.AllocNode;
1577 PatRec^.Token := Token; {save token type}
1578 PatRec^.NextOK := False; {default to non-alternation}
1579 LastPatRec^.NextPattern := PatRec; {hook up the previous token}
1580 if (Token = tknLitChar) or (Token = tknDitto) then begin
1581 PatRec^.OneChar := S[1];
1582 PatRec^.StrPtr := nil;
1583 end else
1584 RaiseStError(EStRegExError, stscUnknownError);
1585 end;
1586
1587
1588 function TStStreamRegEx.MakeReplacePattern(Pattern : PAnsiChar;
1589 Start : Integer;
1590 Delim : AnsiChar;
1591 var PatList : PStPatRecord) : Integer;
1592 {-make a pattern list from arg[i], starting at start, ending at delim}
1593 {return 0 is error, last char position in arg if OK}
1594 var
1595 I : Integer;
1596 PatRec,
1597 LastPatRec : PStPatRecord;
1598 Done : Boolean;
1599 AChar : AnsiChar;
1600
1601 begin
1602 PatList := FNodes.AllocNode;
1603 PatList^.Token := tknNil; {put a nil token at the beginning}
1604 PatList^.NextOK := False;
1605 LastPatRec := PatList;
1606 I := Start; {start point of pattern string}
1607 Done := False;
1608 while not(Done) and (Pattern[I] <> Delim) and (Pattern[I] <> EndStr) do begin
1609 AChar := Pattern[I];
1610 if (AChar = Ditto) then
1611 AddTokenToReplace(PatRec, LastPatRec, tknDitto, '0')
1612 else begin
1613 if (AChar = Esc) then begin
1614 {skip over escape character}
1615 I := Succ(I);
1616 AChar := Pattern[I];
1617 if (AChar >= '1') and (AChar <= '9') then
1618 {a tagged ditto}
1619 AddTokenToReplace(PatRec, LastPatRec, tknDitto, AChar)
1620 else case AChar of
1621 lSpace : AddTokenToReplace(PatRec, LastPatRec, tknLitChar, #32);
1622 lNewline :
1623 begin
1624 AddTokenToReplace(PatRec, LastPatRec, tknLitChar, #13);
1625 LastPatRec := PatRec;
1626 AddTokenToReplace(PatRec, LastPatRec, tknLitChar, #10);
1627 end;
1628 lTab : AddTokenToReplace(PatRec, LastPatRec, tknLitChar, #9);
1629 lBackSpace : AddTokenToReplace(PatRec, LastPatRec, tknLitChar, #8);
1630 lReturn : AddTokenToReplace(PatRec, LastPatRec, tknLitChar, #13);
1631 lFeed : AddTokenToReplace(PatRec, LastPatRec, tknLitChar, #10);
1632 lNil : ;
1633 else
1634 AddTokenToReplace(PatRec, LastPatRec, tknLitChar, AChar);
1635 end;
1636 end else
1637 AddTokenToReplace(PatRec, LastPatRec, tknLitChar, AChar);
1638 end;
1639 LastPatRec := PatRec;
1640 if not(Done) then
1641 Inc(I);
1642 end; {of looking through pattern string}
1643
1644 if Done or (Pattern[I] <> Delim) then begin
1645 Result := 0;
1646 RaiseStError(EStRegExError, stscPatternError);
1647 end else
1648 Result := I;
1649 end;
1650
1651
1652 function TStStreamRegEx.GetReplace(Pattern : PAnsiChar;
1653 var PatList : PStPatRecord) : Boolean;
1654 begin
1655 Result := (MakeReplacePattern(Pattern, 0, EndStr, PatList) > 0);
1656 end;
1657
1658
1659 function TStStreamRegEx.MatchOnePatternElement(var Buf : PAnsiChar;
1660 var I : Integer;
1661 var TagOn : Boolean;
1662 var TagNum : Integer;
1663 PatPtr : PStPatRecord) : Boolean;
1664 {-match one pattern element at pattern pointed to by PatPtr, Buf[I]}
1665 var
1666 Advance : -1..255;
1667 AToken : TStTokens;
1668 PatPos : Integer;
1669 K : Cardinal;
1670 C : AnsiChar;
1671 begin
1672 Advance := -1;
1673 AToken := PatPtr^.Token;
1674 if FIgnoreCase then
1675 C := AnsiChar(AnsiUpperCase(Buf[I])[1])
1676 else
1677 C := Buf[I];
1678
1679 if (C <> EndStr) then begin
1680 if (AToken = tknLitChar) then begin
1681 if (C = PatPtr^.OneChar) then
1682 Advance := 1;
1683 end else if (AToken = tknCharClass) then begin
1684 if (StrChPosS(PatPtr^.StrPtr^, C, K)) then
1685 Advance := 1;
1686 end else if (AToken = tknNegCharClass) then begin
1687 if (not (C in [#13, #10])) then begin
1688 if not (StrChPosS(PatPtr^.StrPtr^, C, K)) then
1689 Advance := 1;
1690 end;
1691 end else if (AToken = tknAnyChar) then begin
1692 if not (C in [#13, #10]) then
1693 Advance := 1;
1694 end else if (AToken = tknBegOfLine) then begin
1695 if (I = 0) then
1696 Advance := 0;
1697 end else if (AToken = tknEndOfLine) then begin
1698 if (C = #13) and (Buf[Succ(I)] = #10) then
1699 Advance := 0;
1700 end else if (AToken = tknNil) then begin
1701 Advance := 0;
1702 end else if (AToken = tknBegTag) then begin
1703 Advance := 0;
1704 if not(TagOn) then begin
1705 TagNum := Succ(TagNum);
1706 TagOn := True;
1707 end;
1708 end else if (AToken = tknEndTag) then begin
1709 Advance := 0;
1710 TagOn := False;
1711 end else if (AToken = tknGroup) then begin
1712 {we treat a group as a "character", but allow advance of multiple chars}
1713 {recursive call to SearchMatchPattern}
1714 PatPos := SearchMatchPattern(Buf, I, TagOn, TagNum, PatPtr^.NestedPattern);
1715 if (PatPos >= I) then begin
1716 I := PatPos;
1717 Advance := 0;
1718 end;
1719 end;
1720 end else begin
1721 {at end of line}
1722 {end tag marks match}
1723 if (AToken = tknEndTag) then
1724 Advance := 0;
1725 end;
1726
1727 if (Advance >= 0) then begin
1728 {ignore tag words here, since they are not used}
1729 Result := True;
1730 Inc(I, Advance);
1731 end else
1732 Result := False;
1733 end;
1734
1735
1736 function TStStreamRegEx.SearchMatchPattern(var Buf : PAnsiChar;
1737 OffSet : Integer;
1738 var TagOn : Boolean;
1739 var TagNum : Integer;
1740 PatPtr : PStPatRecord) : Integer;
1741 {-look for match of pattern list starting at PatPtr with Buf[offset...]}
1742 {-return the last position that matched}
1743 var
1744 I : Integer;
1745 K : Integer;
1746 PatRec : PStPatRecord;
1747 Done : Boolean;
1748 AToken : TStTokens;
1749
1750 begin
1751 Done := False;
1752 PatRec := PatPtr;
1753 while not(Done) and (PatRec <> nil) do begin
1754 AToken := PatRec^.Token;
1755 if (AToken = tknClosure) then begin
1756 {a closure}
1757 PatRec := PatRec^.NextPattern; {step past the closure in the pattern list}
1758 I := OffSet; {leave the current line position unchanged}
1759 {match as many as possible}
1760 while not(Done) and (Buf[I] <> EndStr) do begin
1761 if not(MatchOnePatternElement(Buf, I, TagOn, TagNum, PatRec)) then
1762 Done := True;
1763 end;
1764 {I points to the location that caused a non-match}
1765 {match rest of pattern against rest of input}
1766 {shrink closure by one after each failure}
1767 Done := False;
1768 K := -1;
1769 while not(Done) and (I >= OffSet) do begin
1770 K := SearchMatchPattern(Buf, I, TagOn, TagNum, PatRec^.NextPattern);
1771 if (K > -1) then
1772 Done := True
1773 else
1774 Dec(I);
1775 end;
1776 OffSet := K; {if k=-1 then failure else success}
1777 Done := True;
1778 end else if (AToken = tknMaybeOne) then begin
1779 {a 0 or 1 closure}
1780 PatRec := PatRec^.NextPattern; {step past the closure marker}
1781 {match or no match is ok, but advance lin cursor if matched}
1782 MatchOnePatternElement(Buf, OffSet, TagOn, TagNum, PatRec);
1783 {advance to the next pattern token}
1784 PatRec := PatRec^.NextPattern;
1785 end else if not(MatchOnePatternElement(Buf, OffSet,
1786 TagOn, TagNum, PatRec)) then begin
1787 if PatRec^.NextOK then begin
1788 {we get another chance because of alternation}
1789 PatRec := PatRec^.NextPattern;
1790 end else begin
1791 OffSet := -1;
1792 Done := True;
1793 end;
1794 end else begin
1795 {skip over alternates if we matched already}
1796 while (PatRec^.NextOK) and (PatRec^.NextPattern <> nil) do
1797 PatRec := PatRec^.NextPattern;
1798 {move to the next non-alternate}
1799 PatRec := PatRec^.NextPattern;
1800 end;
1801 end;
1802 Result := OffSet;
1803 end;
1804
1805
1806 function TStStreamRegEx.FindMatch(var Buf : PAnsiChar;
1807 PatPtr : PStPatRecord;
1808 var REPosition : TMatchPosition) : Boolean;
1809 var
1810 I,
1811 LPos,
1812 TagNum : Integer;
1813 TagOn : Boolean;
1814
1815 begin
1816 LPos := -1;
1817 I := 0;
1818 TagNum := 0;
1819 TagOn := False;
1820 Result := False;
1821 REPosition.Length := 0;
1822 while (Buf[I] <> EndStr) and (LPos = -1) do begin
1823 LPos := SearchMatchPattern(Buf, I, TagOn, TagNum, PatPtr);
1824 Result := (LPos > -1);
1825 if (Result) then begin
1826 REPosition.StartPos := I+1;
1827 RePosition.EndPos := LPos;
1828 RePosition.Length := REPosition.EndPos - REPosition.StartPos + 1;
1829 end;
1830 Inc(I);
1831 end;
1832 end;
1833
1834
1835
1836 procedure TStStreamRegEx.InsertLineNumber(Dest : PAnsiChar;
1837 const S : PAnsiChar;
1838 LineNum : Integer);
1839 var
1840 Count : Cardinal;
1841 SI : string[8];
1842 begin
1843 Dest[0] := #0;
1844 Count := StrLen(S);
1845 if (Count > MaxLineLength - 8) then
1846 Count := MaxLineLength - 8;
1847 SI := LeftPadS(IntToStr(LineNum), 6) + ' ';
1848 Move(SI[1], Dest[0], 8);
1849 Move(S^, Dest[8], Count);
1850 Dest[Count+8] := #0;
1851 end;
1852
1853
1854
1855 function TStStreamRegEx.ProcessLine( Buf : PAnsiChar;
1856 Len : integer;
1857 LineNum : integer;
1858 CheckOnly : Boolean;
1859 var REPosition: TMatchPosition) : Boolean;
1860 var
1861 Tmp : PAnsiChar;
1862 begin
1863 GetMem(Tmp, MaxLineLength+1);
1864 try
1865 if (FSelAvoidPatPtr <> nil) then begin
1866 if (not Avoid) then
1867 Result := FindMatch(Buf, FSelAvoidPatPtr, REPosition)
1868 else if (Avoid) then
1869 Result := not(FindMatch(Buf, FSelAvoidPatPtr, REPosition))
1870 else
1871 Result := True;
1872 end else
1873 Result := True;
1874
1875 if Result then begin
1876 {met select criterion, perhaps by default}
1877 FSelectCount := Succ(FSelectCount);
1878 if ((FReplacePatPtr <> nil) and (not CheckOnly)) then begin
1879 if (ooModified in FOutputOptions) then begin
1880 {we only want to replace and output lines that have a match}
1881 Result := FindMatch(Buf, FMatchPatPtr, REPosition);
1882 end;
1883 if Result then begin
1884 Tmp[0] := #0;
1885 SubLine(Buf);
1886 if (not (ooCountOnly in FOutputOptions)) then begin
1887 if (LineNumbers) then
1888 InsertLineNumber(Tmp, FOutlineBuf, LineNum)
1889 else
1890 StrCopy(Tmp, FOutlineBuf);
1891 Tmp[StrLen(Tmp)-2] := #0;
1892 FOutTextStream.WriteLineZ(Tmp);
1893 end;
1894 {subline keeps a count of matched lines and replaced patterns}
1895 end;
1896 end else if (FMatchPatPtr <> nil) then begin
1897 Result := FindMatch(Buf, FMatchPatPtr, REPosition);
1898 {met match criterion}
1899 if Result then begin
1900 FMatchCount := Succ(FMatchCount);
1901 if (not CheckOnly) then begin
1902 if (not (ooCountOnly in FOutputOptions)) then begin
1903 Buf[Len] := #0;
1904 if (LineNumbers) then
1905 InsertLineNumber(Tmp, Buf, LineNum)
1906 else
1907 StrCopy(Tmp, Buf);
1908 Tmp[StrLen(Tmp)] := #0;
1909 FOutTextStream.WriteLineZ(Tmp);
1910 end;
1911 end;
1912 end;
1913 end else begin
1914 {we are neither matching nor replacing, just selecting}
1915 {output the selected line}
1916 if (not CheckOnly) then begin
1917 if (not (ooCountOnly in FOutputOptions)) then begin
1918 Buf[Len] := #0;
1919 if (LineNumbers) then
1920 InsertLineNumber(Tmp, Buf, LineNum)
1921 else
1922 StrCopy(Tmp, Buf);
1923 Tmp[StrLen(Tmp)] := #0;
1924 FOutTextStream.WriteLineZ(Tmp);
1925 end;
1926 end;
1927 end;
1928 end else begin
1929 {non-selected line, do we write it?}
1930 if (ooUnselected in FOutputOptions) and
1931 (not (ooCountOnly in FOutputOptions)) then begin
1932 Buf[Len] := #0;
1933 if (LineNumbers) then
1934 InsertLineNumber(Tmp, Buf, LineNum)
1935 else
1936 StrCopy(Tmp, Buf);
1937 Tmp[StrLen(Tmp)] := #0;
1938 FOutTextStream.WriteLineZ(Tmp);
1939 end;
1940 end;
1941 finally
1942 FreeMem(Tmp, MaxLineLength+1);
1943 end;
1944 end;
1945
1946
1947
1948 procedure TStStreamRegEx.SetMatchPatSL(Value : TStringList);
1949 begin
1950 FMatchPatSL.Assign(Value);
1951 DisposeItems(FMatchPatPtr);
1952 end;
1953
1954
1955
1956 procedure TStStreamRegEx.SetOptions(Value : TStOutputOptions);
1957 begin
1958 if (Value <> FOutputOptions) then begin
1959 FOutputOptions := Value;
1960 if (ooCountOnly in FOutputOptions) then
1961 FOutputOptions := [ooCountOnly];
1962 end;
1963 end;
1964
1965
1966
1967 procedure TStStreamRegEx.SetReplacePatSL(Value : TStringList);
1968 begin
1969 FReplacePatSL.Assign(Value);
1970 DisposeItems(FReplacePatPtr);
1971 end;
1972
1973
1974
1975 procedure TStStreamRegEx.SetSelAvoidPatSL(Value : TStringList);
1976 begin
1977 FSelAvoidPatSL.Assign(Value);
1978 DisposeItems(FSelAvoidPatPtr);
1979 end;
1980
1981
1982 function TStStreamRegEx.SubLineMatchOne(Buf : PAnsiChar;
1983 var Flags : TStFlag;
1984 var TagOn : Boolean;
1985 var I : Integer;
1986 var TagNum : Integer;
1987 PatPtr : PStPatRecord) : Boolean;
1988 var
1989 Advance : -1..255;
1990 lToken : TStTokens;
1991 PatPos : Integer;
1992 K : Cardinal;
1993 C : AnsiChar;
1994 begin
1995 Advance := -1;
1996 lToken := PatPtr^.Token;
1997 if FIgnoreCase then
1998 C := AnsiChar(AnsiUpperCase(Buf[I])[1])
1999 else
2000 C := Buf[I];
2001
2002 if (C <> EndStr) then begin
2003 if (lToken = tknLitChar) then begin
2004 if (C = PatPtr^.OneChar) then
2005 Advance := 1;
2006 end else if (lToken = tknCharClass) then begin
2007 if (StrChPosS(PatPtr^.StrPtr^, C, K)) then
2008 Advance := 1;
2009 end else if (lToken = tknNegCharClass) then begin
2010 if (pos(C, NewLine) = 0) then begin
2011 if not (StrChPosS(PatPtr^.StrPtr^, C, K)) then
2012 Advance := 1;
2013 end;
2014 end else if (lToken = tknAnyChar) then begin
2015 if (not (C in [#13, #10])) then
2016 Advance := 1;
2017 end else if (lToken = tknBegOfLine) then begin
2018 if (I = 0) then
2019 Advance := 0;
2020 end else if (lToken = tknEndOfLine) then begin
2021 if (C = #13) and (Buf[Succ(I)] = #10) then begin
2022 Advance := 0;
2023 end;
2024 end else if (lToken = tknNil) then begin
2025 Advance := 0;
2026 end else if (lToken = tknBegTag) then begin
2027 Advance := 0;
2028 if not(TagOn) then begin
2029 Inc(TagNum);
2030 TagOn := True;
2031 end;
2032 end else if (lToken = tknEndTag) then begin
2033 Advance := 0;
2034 TagOn := False;
2035 end else if (lToken = tknGroup) then begin
2036 {we treat a group as a "character", but allow advance of multiple chars}
2037
2038 PatPos := SubLineMatchPattern(Buf, Flags, TagOn, TagNum,
2039 I, PatPtr^.NestedPattern);
2040 if (PatPos >= I) then begin
2041 I := PatPos;
2042 Advance := 0;
2043 end;
2044 end;
2045 end else begin
2046 {at end of line}
2047 {end tag marks match}
2048 if (lToken = tknEndTag) then
2049 Advance := 0;
2050 end;
2051
2052 if (Advance > 0) then begin
2053 {we had a match at this (these) character position(s)}
2054 {set the match flags}
2055 if (TagOn) then
2056 Flags[I] := TagNum
2057 else
2058 Flags[I] := 0;
2059 Inc(I, Advance);
2060 Result := True;
2061 end else if (Advance = 0) then begin
2062 Result := True;
2063 end else begin
2064 {this character didn't match}
2065 Result := False;
2066 Flags[I] := -1;
2067 end;
2068 end;
2069
2070
2071
2072 function TStStreamRegEx.SubLineMatchPattern(Buf : PAnsiChar;
2073 var Flags : TStFlag;
2074 var TagOn : Boolean;
2075 var TagNum : Integer;
2076 OffSet : Integer;
2077 PatPtr : PStPatRecord) : Integer;
2078 {-look for match of pattern list starting at PatPtr with Buf[offset...]}
2079 {return the last position that matched}
2080 var
2081 I,
2082 LocTag : Integer;
2083 PatPos : Integer;
2084 PatRec : PStPatRecord;
2085 Done : Boolean;
2086 AToken : TStTokens;
2087 OldTagOn : boolean;
2088 OldTagNum: integer;
2089 begin
2090 Done := False;
2091 PatRec := PatPtr;
2092 while not(Done) and (PatRec <> nil) do begin
2093 AToken := PatRec^.Token;
2094 if (AToken = tknClosure) then begin
2095 {a closure}
2096 PatRec := PatRec^.NextPattern; {step past the closure in the pattern list}
2097 I := OffSet; {leave the current line position unchanged}
2098 LocTag := TagNum;
2099 {match as many as possible}
2100 while not(Done) and (Buf[I] <> EndStr) do begin
2101 if not(SubLineMatchOne(Buf, Flags, TagOn,
2102 I, LocTag, PatRec)) then
2103 Done := True;
2104 end;
2105 {i points to the location that caused a non-match}
2106 {match rest of pattern against rest of input}
2107 {shrink closure by one after each failure}
2108 Done := False;
2109 PatPos := -1;
2110 while not(Done) and (I >= OffSet) do begin
2111 OldTagOn := TagOn;
2112 OldTagNum := LocTag;
2113 PatPos := SubLineMatchPattern(Buf, Flags, TagOn,
2114 LocTag, I, PatRec^.NextPattern);
2115 if (PatPos > -1) then
2116 Done := True
2117 else begin
2118 I := Pred(I);
2119 TagOn := OldTagOn;
2120 LocTag := OldTagNum;
2121 end;
2122 end;
2123 OffSet := PatPos; {if k=-1 then failure else success}
2124 TagNum := LocTag;
2125 Done := True;
2126 end else if (AToken = tknMaybeOne) then begin
2127 {a 0 or 1 closure}
2128 PatRec := PatRec^.NextPattern; {step past the closure marker}
2129 {match or no match is ok, but advance lin cursor if matched}
2130 SubLineMatchOne(Buf, Flags, TagOn, OffSet, TagNum, PatRec);
2131 {advance to the next pattern token}
2132 PatRec := PatRec^.NextPattern;
2133 end else if not(SubLineMatchOne(Buf, Flags, TagOn,
2134 OffSet, TagNum, PatRec)) then begin
2135 if PatRec^.NextOK then begin
2136 {we get another chance because of alternation}
2137 PatRec := PatRec^.NextPattern;
2138 end else begin
2139 OffSet := -1;
2140 Done := True;
2141 end;
2142 end else begin
2143 {skip over alternates if we matched already}
2144 while PatRec^.NextOK and (PatRec^.NextPattern <> nil) do
2145 PatRec := PatRec^.NextPattern;
2146 {move to the next non-alternate}
2147 PatRec := PatRec^.NextPattern;
2148 end;
2149 end;
2150 Result := OffSet;
2151 end;
2152
2153
2154 function TStStreamRegEx.SubLineFindTag(Buf : PAnsiChar;
2155 I : Integer;
2156 IEnd : Integer;
2157 TagNum : Integer;
2158 var Flags : TStFlag;
2159 var IStart : Integer;
2160 var IStop : Integer) : Boolean;
2161 {-find the tagged match region}
2162 {return true if it is found}
2163 begin
2164 IStart := I;
2165 while (Buf[IStart] <> EndStr) and (Flags[IStart] <> TagNum) do
2166 Inc(IStart);
2167 if (Flags[IStart] = TagNum) then begin
2168 Result := True;
2169 IStop := IStart;
2170 while (Flags[IStop] = TagNum) and (IStop < IEnd) do
2171 Inc(IStop);
2172 end else
2173 Result := False;
2174 end; {findtag}
2175
2176
2177
2178 procedure TStStreamRegEx.SubLineWrite(Buf : PAnsiChar;
2179 S : PAnsiChar;
2180 RepRec : PStPatRecord;
2181 I,
2182 IEnd : Integer;
2183 var Flags : TStFlag);
2184 {-Write the output line with replacements}
2185 var
2186 TagNum,
2187 IStart,
2188 IStop : Integer;
2189 PatRec : PStPatRecord;
2190 Token : TStTokens;
2191 begin {writesub}
2192 {scan the replacement list}
2193 S[0] := #0;
2194 PatRec := RepRec;
2195 while (PatRec <> nil) do begin
2196 Token := PatRec^.Token;
2197 if (Token = tknDitto) then begin
2198 TagNum := Ord(PatRec^.OneChar)-Ord('0');
2199 if (TagNum = 0) then begin
2200 {untagged ditto}
2201 {add the entire matched region}
2202 AppendS(S, S, @Buf[I], IEnd-I);
2203 end else begin
2204 {tagged ditto}
2205 {find the tagged region}
2206
2207 if SubLineFindTag(Buf, I, IEnd, TagNum, Flags, IStart, IStop) then begin
2208 {add the tagged region}
2209 AppendS(S, S, @Buf[IStart], IStop-IStart);
2210 end else begin
2211 {else couldn't find tagged word, don't append anything}
2212 end;
2213 end;
2214 end else if (Token = tknLitChar) then
2215 AppendS(S, S, @PatRec^.OneChar, 1);
2216 PatRec := PatRec^.NextPattern;
2217 end;
2218 end;
2219
2220
2221
2222 procedure TStStreamRegEx.SubLine(Buf : PAnsiChar);
2223 var
2224 I,
2225 M,
2226 NumToAdd,
2227 TagNum,
2228 Lastm : Integer;
2229
2230 Flags : TStFlag;
2231 TagOn,
2232 DidReplace : Boolean;
2233 ALine : PAnsiChar;
2234 begin
2235 DidReplace := False;
2236 LastM := -1;
2237 I := 0;
2238
2239 GetMem(ALine, MaxLineLength+1);
2240 try
2241 FOutLineBuf[0] := #0;
2242 FillChar(ALine^, MaxLineLength+1, #0);
2243 while (Buf[I] <> EndStr) do begin
2244 TagNum := 0;
2245 TagOn := False;
2246
2247 M := SubLineMatchPattern(Buf, Flags, TagOn, TagNum, I, FMatchPatPtr);
2248 if (M > -1) and (M <> I) and (LastM <> M) then begin
2249 {keep track of count}
2250 DidReplace := True;
2251 Inc(FReplaceCount);
2252 {replace matched text}
2253
2254 SubLineWrite(Buf, ALine, FReplacePatPtr, I, M, Flags);
2255 LastM := M;
2256 AppendS(FOutLineBuf, FOutLineBuf, ALine, StrLen(ALine));
2257 end;
2258
2259 if (M = -1) or (M = I) then begin
2260 {no match or null match, append the character}
2261 if (Buf[I] = #13) then
2262 NumToAdd := 2
2263 else
2264 NumToAdd := 1;
2265 AppendS(FOutLineBuf, FOutLineBuf, @Buf[I], NumToAdd);
2266 I := I + NumToAdd;
2267 end else {skip matched text}
2268 I := M;
2269
2270 end;
2271 if DidReplace then
2272 Inc(FMatchCount);
2273 finally
2274 FreeMem(ALine, MaxLineLength+1)
2275 end;
2276 end;
2277
2278
2279 {******************************************************************************}
2280 { TStRegEx Implementation }
2281 {******************************************************************************}
2282
2283 constructor TStRegEx.Create(AOwner : TComponent);
2284 begin
2285 inherited Create(AOwner);
2286
2287 FAvoid := False;
2288 FIgnoreCase := False;
2289 FLineNumbers := False;
2290 FOutputOptions := [];
2291
2292 FInLineTerminator := ltCRLF;
2293 FInLineTermChar := #10;
2294 FInFixedLineLength:= 80;
2295
2296 FOutLineTerminator := ltCRLF;
2297 FOutLineTermChar := #10;
2298 FOutFixedLineLength := 80; {not used straight away}
2299
2300 FMaxLineLength := 1024;
2301
2302 FMatchPatSL := TStringList.Create;
2303 FMatchPatPtr := nil;
2304 FSelAvoidPatSL := TStringList.Create;
2305 FSelAvoidPatPtr:= nil;
2306 FReplacePatSL := TStringList.Create;
2307 FReplacePatPtr := nil;
2308
2309 FInFileStream := nil;
2310 FOutFileStream := nil;
2311
2312 FStream := TStStreamRegEx.Create;
2313 end;
2314
2315
2316 destructor TStRegEx.Destroy;
2317 begin
2318 FMatchPatSL.Free;
2319 FMatchPatSL := nil;
2320
2321 FReplacePatSL.Free;
2322 FReplacePatSL := nil;
2323
2324 FSelAvoidPatSL.Free;
2325 FSelAvoidPatSL := nil;
2326
2327 FStream.Free;
2328 FStream := nil;
2329
2330 inherited Destroy;
2331 end;
2332
2333
2334 function TStRegEx.CheckString(const S : AnsiString;
2335 var REPosition : TMatchPosition) : Boolean;
2336 begin
2337 if (Assigned(FStream)) then begin
2338 SetStreamProperties;
2339 Result := FStream.CheckString(S, REPosition);
2340 end else
2341 Result := False;
2342 end;
2343
2344
2345 function TStRegEx.ReplaceString(var S : AnsiString;
2346 var REPosition : TMatchPosition) : Boolean;
2347 begin
2348 if (Assigned(FStream)) then begin
2349 SetStreamProperties;
2350 Result := FStream.ReplaceString(S, REPosition);
2351 end else
2352 Result := False;
2353 end;
2354
2355
2356 function TStRegEx.FileMasksToRegEx(const Masks : AnsiString) : Boolean;{!!.02}
2357 begin
2358 if (Assigned(FStream)) then begin
2359 SetStreamProperties;
2360 Result := FStream.FileMasksToRegEx(Masks);
2361 if (Result) then
2362 FMatchPatSL.Assign(FStream.FMatchPatSL);
2363 end else
2364 Result := False;
2365 end;
2366
2367
2368 function TStRegEx.Execute : Boolean;
2369 begin
2370 Result := False;
2371 try
2372 if (not FileExists(FInputFile)) then
2373 RaiseStError(EStRegExError, stscInFileNotFound);
2374
2375 try
2376 FInFileStream := TFileStream.Create(FInputFile,
2377 fmOpenRead or fmShareDenyWrite);
2378 FStream.InputStream := FInFileStream
2379 except
2380 RaiseStError(EStRegExError, stscREInFileError);
2381 Exit;
2382 end;
2383
2384 if not (ooCountOnly in OutputOptions) then begin
2385 if (FileExists(FOutputFile)) then
2386 try
2387 SysUtils.DeleteFile(FOutputFile);
2388 except
2389 RaiseStError(EStRegExError, stscOutFileDelete);
2390 Exit;
2391 end;
2392
2393 FOutFileStream := nil;
2394 FStream.OutputStream := nil;
2395 try
2396 FOutFileStream := TFileStream.Create(FOutputFile, fmCreate);
2397 FStream.OutputStream := FOutFileStream
2398 except
2399 RaiseStError(EStRegExError, stscOutFileCreate);
2400 Exit;
2401 end;
2402 end;
2403
2404 SetStreamProperties;
2405 Result := FStream.Execute;
2406
2407 FMatchCount := FStream.FMatchCount;
2408 FSelectCount := FStream.FSelectCount;
2409 FReplaceCount := FStream.FReplaceCount;
2410 FInLineCount := FStream.FInLineCount;
2411 FLinesPerSec := FStream.FLinesPerSec;
2412 finally
2413 FInFileStream.Free;
2414 FInFileStream := nil;
2415
2416 FOutFileStream.Free;
2417 FOutFileStream := nil;
2418 end;
2419 end;
2420
2421
2422
2423 procedure TStRegEx.SetMatchPatSL(Value : TStringList);
2424 begin
2425 FMatchPatSL.Assign(Value);
2426 end;
2427
2428
2429
2430 procedure TStRegEx.SetOptions(Value : TStOutputOptions);
2431 begin
2432 if (Value <> FOutputOptions) then begin
2433 FOutputOptions := Value;
2434 if (ooCountOnly in FOutputOptions) then
2435 FOutputOptions := [ooCountOnly];
2436 end;
2437 end;
2438
2439
2440
2441 procedure TStRegEx.SetReplacePatSL(Value : TStringList);
2442 begin
2443 FReplacePatSL.Assign(Value);
2444 end;
2445
2446
2447
2448 procedure TStRegEx.SetSelAvoidPatSL(Value : TStringList);
2449 begin
2450 FSelAvoidPatSL.Assign(Value);
2451 end;
2452
2453
2454
2455 procedure TStRegEx.SetStreamProperties;
2456 begin
2457 if (not Assigned(FStream)) then Exit;
2458
2459 FStream.InLineTermChar := FInLineTermChar;
2460 FStream.InLineTerminator := FInLineTerminator;
2461 FStream.InFixedLineLength := FInFixedLineLength;
2462 {!!.02 - Changed }
2463 // FStream.InLineTermChar := FOutLineTermChar;
2464 // FStream.InLineTerminator := FOutLineTerminator;
2465 // FStream.InFixedLineLength := FOutFixedLineLength;
2466 FStream.OutLineTermChar := FOutLineTermChar;
2467 FStream.OutLineTerminator := FOutLineTerminator;
2468 FStream.OutFixedLineLength := FOutFixedLineLength;
2469 {!!.02 - Changed end }
2470
2471 FStream.Avoid := FAvoid;
2472 FStream.IgnoreCase := FIgnoreCase;
2473 FStream.LineNumbers := FLineNumbers;
2474 FStream.MatchPattern := FMatchPatSL;
2475 FStream.OnMatch := FOnMatch;
2476 FStream.OnProgress := FOnProgress;
2477 FStream.OutputOptions := FOutputOptions;
2478 FStream.ReplacePattern := FReplacePatSL;
2479 FStream.SelAvoidPattern:= FSelAvoidPatSL;
2480
2481 FStream.FMatchCount := 0;
2482 FStream.FSelectCount := 0;
2483 FStream.FReplaceCount := 0;
2484 FStream.FInLineCount := 0;
2485 FStream.FLinesPerSec := 0;
2486 end;
2487
2488
2489 end.
2490

  ViewVC Help
Powered by ViewVC 1.1.20