{
  pcRegExp - Perl compatible regular expressions for Virtual Pascal
  (c) 2001 Peter S. Voronov aka Chem O'Dun <petervrn@yahoo.com>

  Based on PCRE library interface unit for Virtual Pascal.
  (c) 2001 Alexander Tokarev <dwalin@dwalin.ru>

  The current PCRE version is: 3.7

  This software must be distributed as Freeware.

  The PCRE library is written by: Philip Hazel <ph10@cam.ac.uk>
  Copyright (c) 1997-2004 University of Cambridge

  AngelsHolocaust 4-11-04 updated to use version v5.0
  (INFO: this is regex-directed, NFA)
  AH:  9-11-04 - pcre_free: removed var, pcre already gives the ptr, now
			    everything works as it should (no more crashes)
		 -> removed CheckRegExp because pcre handles errors perfectly
      10-11-04 - added pcError (errorhandling), pcInit
      13-11-04 - removed the ErrorPos = 0 check -> always print erroroffset
      17-10-05 - support for \1-\9 backreferences in TpcRegExp.GetReplStr
      17-02-06 - added RunTimeOptions: caller can set options while searching
      19-02-06 - added SearchOfs(): let PCRE use the complete string and offset
		 into the string itself
      20-12-06 - support for version 7.0
}

{$H+} {$DEFINE PCRE_3_7} {$DEFINE PCRE_5_0} {$DEFINE PCRE_7_0}

Unit pcregexp;

Interface

uses objects;

Type
 PpcRegExp = ^TpcRegExp;
// TpcRegExp = object
 TpcRegExp = object(TObject)
  MatchesCount: integer;
  RegExpC, RegExpExt : Pointer;
  Matches:Pointer;
  RegExp: shortstring;
  SourceLen: integer;
  PartialMatch : boolean;
  Error : boolean;
  ErrorMsg : Pchar;
  ErrorPos : integer;
  RunTimeOptions: Integer; // options which can be set by the caller
  constructor Init(const ARegExp : shortstring; AOptions : integer; ALocale : Pointer);
  function Search(AStr: Pchar; ALen : longint) : boolean; virtual;
  function SearchNext( AStr: Pchar; ALen : longint) : boolean; virtual;
  function SearchOfs ( AStr: Pchar; ALen, AOfs : longint) : boolean; virtual;
  function MatchSub(ANom: integer; var Pos, Len : longint) : boolean; virtual;
  function MatchFull(var Pos, Len : longint) : boolean; virtual;
  function GetSubStr(ANom: integer; AStr: Pchar) : string; virtual;
  function GetFullStr(AStr: Pchar) : string; virtual;
  function GetReplStr(AStr: Pchar; const ARepl: string) : string; virtual;
  function GetPreSubStr(AStr: Pchar) : string; virtual;
  function GetPostSubStr(AStr: Pchar) : string; virtual;
  function ErrorStr : string; virtual;
  destructor Done; virtual;
 end;

 function pcGrepMatch(WildCard, aStr: string; AOptions:integer; ALocale : Pointer): Boolean;
 function pcGrepSub(WildCard, aStr, aRepl: string; AOptions:integer; ALocale : Pointer): string;

 function pcFastGrepMatch(WildCard, aStr: string): Boolean;
 function pcFastGrepSub(WildCard, aStr, aRepl: string): string;

{$IFDEF PCRE_5_0}
 function pcGetVersion : pchar;
{$ENDIF}

 function pcError (var pRegExp : Pointer) : Boolean;
 function pcInit  (const Pattern: Shortstring; CaseSens: Boolean) : Pointer;

Const { Options }
 PCRE_CASELESS         = $0001;
 PCRE_MULTILINE        = $0002;
 PCRE_DOTALL           = $0004;
 PCRE_EXTENDED         = $0008;
 PCRE_ANCHORED         = $0010;
 PCRE_DOLLAR_ENDONLY   = $0020;
 PCRE_EXTRA            = $0040;
 PCRE_NOTBOL           = $0080;
 PCRE_NOTEOL           = $0100;
 PCRE_UNGREEDY         = $0200;
 PCRE_NOTEMPTY         = $0400;
{$IFDEF PCRE_5_0}
 PCRE_UTF8             = $0800;
 PCRE_NO_AUTO_CAPTURE  = $1000;
 PCRE_NO_UTF8_CHECK    = $2000;
 PCRE_AUTO_CALLOUT     = $4000;
 PCRE_PARTIAL          = $8000;
{$ENDIF}
{$IFDEF PCRE_7_0}
 PCRE_DFA_SHORTEST     = $00010000;
 PCRE_DFA_RESTART      = $00020000;
 PCRE_FIRSTLINE        = $00040000;
 PCRE_DUPNAMES         = $00080000;
 PCRE_NEWLINE_CR       = $00100000;
 PCRE_NEWLINE_LF       = $00200000;
 PCRE_NEWLINE_CRLF     = $00300000;
 PCRE_NEWLINE_ANY      = $00400000;
 PCRE_NEWLINE_ANYCRLF  = $00500000;
{$ENDIF}

 PCRE_COMPILE_ALLOWED_OPTIONS = PCRE_ANCHORED + PCRE_AUTO_CALLOUT + PCRE_CASELESS  +
				PCRE_DOLLAR_ENDONLY + PCRE_DOTALL + PCRE_EXTENDED  +
				PCRE_EXTRA + PCRE_MULTILINE + PCRE_NO_AUTO_CAPTURE +
				PCRE_UNGREEDY + PCRE_UTF8 + PCRE_NO_UTF8_CHECK
				{$IFDEF PCRE_7_0}
				+ PCRE_DUPNAMES + PCRE_FIRSTLINE + PCRE_NEWLINE_CRLF
				+ PCRE_NEWLINE_ANY + PCRE_NEWLINE_CRLF
				{$ENDIF}
				;

 PCRE_EXEC_ALLOWED_OPTIONS = PCRE_ANCHORED + PCRE_NOTBOL + PCRE_NOTEOL +
			     PCRE_NOTEMPTY + PCRE_NO_UTF8_CHECK + PCRE_PARTIAL
			     {$IFDEF PCRE_7_0}
                             + PCRE_NEWLINE_CRLF + PCRE_NEWLINE_ANY +PCRE_NEWLINE_ANYCRLF
			     {$ENDIF}
			     ;

{$IFDEF PCRE_7_0}
 PCRE_DFA_EXEC_ALLOWED_OPTIONS = PCRE_ANCHORED + PCRE_NOTBOL + PCRE_NOTEOL +
				 PCRE_NOTEMPTY + PCRE_NO_UTF8_CHECK + PCRE_PARTIAL +
				 PCRE_DFA_SHORTEST + PCRE_DFA_RESTART +
				 PCRE_NEWLINE_CR + PCRE_NEWLINE_LF + PCRE_NEWLINE_CRLF +
				 PCRE_NEWLINE_ANY + PCRE_NEWLINE_ANYCRLF
{$ENDIF}

{ Exec-time and get/set-time error codes }
 PCRE_ERROR_NOMATCH        =  -1;
 PCRE_ERROR_NULL	   =  -2;
 PCRE_ERROR_BADOPTION      =  -3;
 PCRE_ERROR_BADMAGIC       =  -4;
 PCRE_ERROR_UNKNOWN_MODE   =  -5;
 PCRE_ERROR_NOMEMORY       =  -6;
 PCRE_ERROR_NOSUBSTRING    =  -7;
{$IFDEF PCRE_5_0}
 PCRE_ERROR_MATCHLIMIT     =  -8;
 PCRE_ERROR_CALLOUT        =  -9;  { Never used by PCRE itself }
 PCRE_ERROR_BADUTF8        = -10;
 PCRE_ERROR_BADUTF8_OFFSET = -11;
 PCRE_ERROR_PARTIAL        = -12;
 PCRE_ERROR_BADPARTIAL     = -13;
 PCRE_ERROR_INTERNAL       = -14;
 PCRE_ERROR_BADCOUNT       = -15;
{$ENDIF}
{$IFDEF PCRE_7_0}
 PCRE_ERROR_DFA_UITEM      = -16;
 PCRE_ERROR_DFA_UCOND      = -17;
 PCRE_ERROR_DFA_UMLIMIT    = -18;
 PCRE_ERROR_DFA_WSSIZE     = -19;
 PCRE_ERROR_DFA_RECURSE    = -20;
 PCRE_ERROR_RECURSIONLIMIT = -21;
 PCRE_ERROR_NULLWSLIMIT    = -22;
 PCRE_ERROR_BADNEWLINE     = -23;
{$ENDIF}

{ Request types for pcre_fullinfo() }

 PCRE_INFO_OPTIONS         =  0;
 PCRE_INFO_SIZE		   =  1;
 PCRE_INFO_CAPTURECOUNT    =  2;
 PCRE_INFO_BACKREFMAX      =  3;
 PCRE_INFO_FIRSTBYTE       =  4;
 PCRE_INFO_FIRSTCHAR       =  4; { For backwards compatibility }
 PCRE_INFO_FIRSTTABLE      =  5;
{$IFDEF PCRE_5_0}
 PCRE_INFO_LASTLITERAL     =  6;
 PCRE_INFO_NAMEENTRYSIZE   =  7;
 PCRE_INFO_NAMECOUNT       =  8;
 PCRE_INFO_NAMETABLE       =  9;
 PCRE_INFO_STUDYSIZE       = 10;
 PCRE_INFO_DEFAULT_TABLES  = 11;
{$ENDIF PCRE_5_0}

{ Request types for pcre_config() }
{$IFDEF PCRE_5_0}
 PCRE_CONFIG_UTF8		    = 0;
 PCRE_CONFIG_NEWLINE		    = 1;
 PCRE_CONFIG_LINK_SIZE		    = 2;
 PCRE_CONFIG_POSIX_MALLOC_THRESHOLD = 3;
 PCRE_CONFIG_MATCH_LIMIT	    = 4;
 PCRE_CONFIG_STACKRECURSE           = 5;
 PCRE_CONFIG_UNICODE_PROPERTIES     = 6;
{$ENDIF PCRE_5_0}
{$IFDEF PCRE_7_0}
 PCRE_CONFIG_MATCH_LIMIT_RECURSION  = 7;
{$ENDIF}

{ Bit flags for the pcre_extra structure }
{$IFDEF PCRE_5_0}
 PCRE_EXTRA_STUDY_DATA		  = $0001;
 PCRE_EXTRA_MATCH_LIMIT		  = $0002;
 PCRE_EXTRA_CALLOUT_DATA	  = $0004;
 PCRE_EXTRA_TABLES		  = $0008;
{$ENDIF PCRE_5_0}
{$IFDEF PCRE_7_0}
 PCRE_EXTRA_MATCH_LIMIT_RECURSION = $0010;
{$ENDIF}

Const
// DefaultOptions : integer = 0;
 DefaultLocaleTable : pointer = nil;

{$IFDEF PCRE_5_0}
{ The structure for passing additional data to pcre_exec(). This is defined in
such as way as to be extensible. Always add new fields at the end, in order to
remain compatible. }

type ppcre_extra = ^tpcre_extra;
     tpcre_extra = record
       flags : longint;		       { Bits for which fields are set }
       study_data : pointer;           { Opaque data from pcre_study() }
       match_limit : longint;          { Maximum number of calls to match() }
       callout_data : pointer;         { Data passed back in callouts }
       tables : pointer;	       { Pointer to character tables }
       match_limit_recursion: longint; { Max recursive calls to match() }
     end;

type ppcre_callout_block = ^pcre_callout_block;
     pcre_callout_block = record
       version,
  (* ------------------------ Version 0 ------------------------------- *)
       callout_number : integer;
       offset_vector : pointer;
       subject : pchar;
       subject_length, start_match, current_position, capture_top,
       capture_last : integer;
       callout_data : pointer;
  (* ------------------- Added for Version 1 -------------------------- *)
       pattern_position, next_item_length : integer;
     end;
{$ENDIF PCRE_5_0}

{$OrgName+}
{$IFDEF VIRTUALPASCAL} {&Cdecl+} {$ENDIF VIRTUALPASCAL}

 { local replacement of external pcre memory management functions }
 function pcre_malloc( size : integer ) : pointer;
 procedure pcre_free( {var} p : pointer );
{$IFDEF PCRE_5_0}
 const pcre_stack_malloc: function ( size : integer ): pointer = pcre_malloc;
       pcre_stack_free: procedure ( {var} p : pointer ) = pcre_free;
 function pcre_callout(var p : ppcre_callout_block) : integer;
{$ENDIF PCRE_5_0}
{$IFDEF VIRTUALPASCAL} {&Cdecl-} {$ENDIF VIRTUALPASCAL}

Implementation

Uses strings, collect, messages, dnapp, commands, advance0, stringsx
    {$IFDEF VIRTUALPASCAL} ,vpsyslow {$ENDIF VIRTUALPASCAL};

Const
 MAGIC_NUMBER = $50435245; { 'PCRE' }
 MAX_MATCHES = 90; { changed in 3.5 version; should be divisible by 3, was 64}

Type
 PMatchArray = ^TMatchArray;
 TMatchArray = array[0..( MAX_MATCHES * 3 )] of integer;

 PRegExpCollection = ^TRegExpCollection;
 TRegExpCollection =  object(TSortedCollection)
   MaxRegExp : integer;
   SearchRegExp : shortstring;
   CompareModeInsert : boolean;
   constructor Init(AMaxRegExp:integer);
   procedure FreeItem(P: Pointer); virtual;
   function  Compare(P1, P2: Pointer): Integer; virtual;
   function  Find(ARegExp:shortstring;var P: PpcRegExp):boolean; virtual;
   function CheckNew(ARegExp:shortstring):PpcRegExp;virtual;
 end;

Var
 PRegExpCache : PRegExpCollection;


{$IFDEF VIRTUALPASCAL} {&Cdecl+} {$ENDIF VIRTUALPASCAL}

 { imported original pcre functions }

 function pcre_compile( const pattern : PChar; options : integer;
			var errorptr : PChar; var erroroffset : integer;
			const tables : PChar ) : pointer {pcre}; external;
{$IFDEF PCRE_7_0}
 function pcre_compile2( const pattern : PChar; options : integer;
			 var errorcodeptr : Integer;
			 var errorptr : PChar; var erroroffset : integer;
			 const tables : PChar ) : pointer {pcre}; external;
{$ENDIF}
{$IFDEF PCRE_5_0}
 function pcre_config( what : integer; where : pointer) : integer; external;
 function pcre_copy_named_substring( const code : pointer {pcre};
				     const subject : pchar;
				     var ovector : integer;
				     stringcount : integer;
				     const stringname : pchar;
				     var buffer : pchar;
				     size : integer) : integer; external;
 function pcre_copy_substring( const subject : pchar; var ovector : integer;
			       stringcount, stringnumber : integer;
			       var buffer : pchar; size : integer )
			       : integer; external;
 function pcre_exec( const argument_re : pointer {pcre};
		     const extra_data : pointer {pcre_extra};
{$ELSE}
 function pcre_exec( const external_re : pointer;
		     const external_extra : pointer;
{$ENDIF}
		     const subject : PChar;
		     length, start_offset, options : integer;
		     offsets : pointer;
		     offsetcount : integer ) : integer; external;
{$IFDEF PCRE_7_0}
 function pcre_dfa_exec( const argument_re : pointer {pcre};
			 const extra_data : pointer {pcre_extra};
			 const subject : pchar;
			 length, start_offset, options : integer;
			 offsets : pointer;
			 offsetcount : integer;
			 workspace : pointer;
			 wscount : integer ) : integer; external;
{$ENDIF}
{$IFDEF PCRE_5_0}
 procedure pcre_free_substring( const p : pchar ); external;
 procedure pcre_free_substring_list( var p : pchar ); external;
 function pcre_fullinfo( const argument_re : pointer {pcre};
			 const extra_data : pointer {pcre_extra};
			 what : integer;
			 where : pointer ) : integer; external;
 function pcre_get_named_substring( const code : pointer {pcre};
				    const subject : pchar;
				    var ovector : integer;
				    stringcount : integer;
				    const stringname : pchar;
				    var stringptr : pchar ) : integer; external;
 function pcre_get_stringnumber( const code : pointer {pcre};
				 const stringname : pchar ) : integer; external;
 function pcre_get_stringtable_entries( const code : pointer {pcre};
					const stringname : pchar;
					var firstptr,
					    lastptr : pchar ) : integer; external;
 function pcre_get_substring( const subject : pchar; var ovector : integer;
			      stringcount, stringnumber : integer;
			      var stringptr : pchar ) : integer; external;
 function pcre_get_substring_list( const subject : pchar; var ovector : integer;
				   stringcount : integer;
				   listptr : pointer {const char ***listptr}) : integer; external;
 function pcre_info( const argument_re : pointer {pcre};
		     var optptr : integer;
		     var first_byte : integer ) : integer; external;
 function pcre_maketables : pchar; external;
{$ENDIF}
{$IFDEF PCRE_7_0}
 function pcre_refcount( const argument_re : pointer {pcre};
			 adjust : integer ) : pchar; external;
{$ENDIF}
 function pcre_study( const external_re : pointer {pcre};
		      options : integer;
		      var errorptr : PChar ) : pointer {pcre_extra}; external;
{$IFDEF PCRE_5_0}
 function pcre_version : pchar; external;
{$ENDIF}

 function pcre_malloc( size : integer ) : pointer;
 begin
  GetMem( result, size );
 end;

 procedure pcre_free( {var} p : pointer );
 begin
  if (p <> nil) then
    FreeMem( p, 0 );
  {@p := nil;}
 end;

{$IFDEF PCRE_5_0}
(* Called from PCRE as a result of the (?C) item. We print out where we are in
the match. Yield zero unless more callouts than the fail count, or the callout
data is not zero. *)

 function pcre_callout;
 begin
 end;
{$ENDIF}

{$IFDEF VIRTUALPASCAL} {&Cdecl-} {$ENDIF VIRTUALPASCAL}

// Always include the newest version of the library
{$IFDEF PCRE_3_7} {$IFNDEF PCRE_5_0} {$IFNDEF PCRE_7_0} {$L pcre37.lib} {$ENDIF PCRE_7_0} {$ENDIF PCRE_5_0} {$ENDIF PCRE_3_7}
{$IFDEF PCRE_5_0}		     {$IFNDEF PCRE_7_0} {$L pcre50.lib} {$ENDIF PCRE_7_0}		    {$ENDIF PCRE_5_0}
{$IFDEF PCRE_7_0}					{$L pcre70.lib}					    {$ENDIF PCRE_7_0}

{TpcRegExp}

 constructor TpcRegExp.Init(const ARegExp:shortstring; AOptions:integer; ALocale : Pointer);
 var
  pRegExp : PChar;
 begin
  RegExp:=ARegExp;
  RegExpC:=nil;
  RegExpExt:=nil;
  Matches:=nil;
  MatchesCount:=0;
  Error:=true;
  ErrorMsg:=nil;
  ErrorPos:=0;
  RunTimeOptions := 0;
  if length(RegExp) < 255 then
   begin
    RegExp[length(RegExp)+1]:=#0;
    pRegExp:=@RegExp[1];
   end
  else
   begin
    GetMem(pRegExp,length(RegExp)+1);
    pRegExp:=strpcopy(pRegExp,RegExp);
   end;
  RegExpC := pcre_compile( pRegExp,
			   AOptions and PCRE_COMPILE_ALLOWED_OPTIONS,
			   ErrorMsg, ErrorPos, ALocale);
  if length(RegExp) = 255 then
   StrDispose(pRegExp);
  if RegExpC = nil then
   exit;
  ErrorMsg:=nil;
  RegExpExt := pcre_study( RegExpC, 0, ErrorMsg );
  if (RegExpExt = nil) and (ErrorMsg <> nil) then
   begin
    pcre_free(RegExpC);
    exit;
   end;
  GetMem(Matches,SizeOf(TMatchArray));
  Error:=false;
 end;

 destructor TpcRegExp.Done;
 begin
  if RegExpC <> nil then
    pcre_free(RegExpC);
  if RegExpExt <> nil then
    pcre_free(RegExpExt);
  if Matches <> nil then
    FreeMem(Matches,SizeOf(TMatchArray));
 end;

 function TpcRegExp.SearchNext( AStr: Pchar; ALen : longint ) : boolean;
 var Options: Integer;
 begin // must handle PCRE_ERROR_PARTIAL here
  Options := (RunTimeOptions or startup.MiscMultiData.cfgRegEx.DefaultOptions) and
	     PCRE_EXEC_ALLOWED_OPTIONS;
  if MatchesCount > 0 then
    MatchesCount:=pcre_exec( RegExpC, RegExpExt, AStr, ALen, PMatchArray(Matches)^[1],
			     Options, Matches, MAX_MATCHES ) else
    MatchesCount:=pcre_exec( RegExpC, RegExpExt, AStr, ALen, 0,
			     Options, Matches, MAX_MATCHES );
{  if MatchesCount = 0 then
    MatchesCount := MatchesCount div 3;}
  PartialMatch := MatchesCount = PCRE_ERROR_PARTIAL;
  SearchNext := MatchesCount > 0;
 end;

 function TpcRegExp.Search( AStr: Pchar; ALen : longint):boolean;
 begin
  MatchesCount:=0;
  Search:=SearchNext(AStr,ALen);
  SourceLen:=ALen;
 end;

 function TpcRegExp.SearchOfs( AStr: Pchar; ALen, AOfs: longint ) : boolean;
 var Options: Integer;
 begin
  MatchesCount:=0;
  Options := (RunTimeOptions or startup.MiscMultiData.cfgRegEx.DefaultOptions) and
	     PCRE_EXEC_ALLOWED_OPTIONS;
  MatchesCount:=pcre_exec( RegExpC, RegExpExt, AStr, ALen, AOfs,
			   Options, Matches, MAX_MATCHES );
  PartialMatch := MatchesCount = PCRE_ERROR_PARTIAL;
  SearchOfs := MatchesCount > 0;
  SourceLen := ALen-AOfs;
 end;

 function TpcRegExp.MatchSub(ANom:integer; var Pos,Len:longint):boolean;
 begin
  if (MatchesCount > 0) and (ANom <= (MatchesCount-1)) then
   begin
    ANom:=ANom*2;
    Pos:=PMatchArray(Matches)^[ANom];
    Len:=PMatchArray(Matches)^[ANom+1]-Pos;
    MatchSub:=true;
   end
  else
   MatchSub:=false;
 end;

 function TpcRegExp.MatchFull(var Pos,Len:longint):boolean;
 begin
  MatchFull:=MatchSub(0,Pos,Len);
 end;

 function TpcRegExp.GetSubStr(ANom: integer; AStr: Pchar):string;
 var
  s: ansistring;
  pos,len: longint;
 begin
  s:='';
  if MatchSub(ANom, pos, len) then
   begin
    setlength(s, len);
    Move(AStr[pos], s[1], len);
   end;
  GetSubStr:=s;
 end;

 function TpcRegExp.GetPreSubStr(AStr: Pchar):string;
 var
  s: ansistring;
  l: longint;
 begin
  s:='';
  if (MatchesCount > 0) then
   begin
    l:=PMatchArray(Matches)^[0]-1;
    if l > 0 then
     begin
      setlength(s,l);
      Move(AStr[1],s[1],l);
     end;
   end;
  GetPreSubStr:=s;
 end;

 function TpcRegExp.GetPostSubStr(AStr: Pchar):string;
 var
  s: ansistring;
  l: longint;
  ANom: integer;
 begin
  s:='';
  if (MatchesCount > 0) then
   begin
    ANom:=(MatchesCount-1){*2} shl 1;
    l:=SourceLen-PMatchArray(Matches)^[ANom+1]+1;
    if l > 0 then
     begin
      setlength(s,l);
      Move(AStr[PMatchArray(Matches)^[ANom+1]],s[1],l);
     end;
   end;
  GetPostSubStr:=s;
 end;


 function TpcRegExp.GetFullStr(AStr: Pchar):string;
 var
  s: ansistring;
  l: longint;
 begin
  GetFullStr:=GetSubStr(0,AStr);
 end;

 function TpcRegExp.GetReplStr(AStr: Pchar; const ARepl: string):string;
 var
  s: ansistring;
  l,i,lasti: longint;
 begin
  l:=length(ARepl);
  i:=1;
  lasti:=1;
  s:='';
  while i <= l do
   begin
    case ARepl[i] of
     '\' :
      begin
       if i < l then
	begin
	 s:=s+copy(ARepl,lasti,i-lasti){+ARepl[i+1]};
	 {AH 17-10-05 support for POSIX \1-\9 backreferences}
	 case ARepl[i+1] of
	  '0' : s:=s+GetFullStr(AStr);
	  '1'..'9' : s:=s+GetSubStr(ord(ARepl[i+1])-ord('0'),AStr);
	  else s:=s+ARepl[i+1]; // copy the escaped character
	 end;
	end;
       inc(i);
       lasti:=i+1;
      end;
     '$' :
      begin
       if i < l then
	begin
	 s:=s+copy(ARepl,lasti,i-lasti);
	 case ARepl[i+1] of
	  '&' : s:=s+GetFullStr(AStr);
	  '1'..'9' : s:=s+GetSubStr(ord(ARepl[i+1])-ord('0'),AStr);
	  '`' : s:=s+GetPreSubStr(AStr);
	  #39 : s:=s+GetPostSubStr(AStr);
	 end;
	end;
       inc(i);
       lasti:=i+1;
      end;
    end;
    inc(i);
   end;
  if lasti <= {AH 25-10-2004 added =, else l==1 won't work} l then
    s:=s+copy(ARepl,lasti,l-lasti+1);
  GetReplStr:=s;
 end;

 function TpcRegExp.ErrorStr:string;
  begin
   ErrorStr:=StrPas(ErrorMsg);
  end;

{TRegExpCollection}

constructor TRegExpCollection.Init(AMaxRegExp: integer);
begin
 Inherited Init(1,1);
 MaxRegExp:=AMaxRegExp;
 CompareModeInsert:=true;
end;

procedure TRegExpCollection.FreeItem(P: Pointer);
begin
 if P <> nil then
  begin
   Dispose(PpcRegExp(P),Done);
  end;
end;

function  TRegExpCollection.Compare(P1, P2: Pointer): Integer;
//var
// l,l1,l2,i : byte;
//// wPos: pchar;
begin
 if CompareModeInsert then
  begin
//   l1:=length(PpcRegExp(P1)^.RegExp);
//   l2:=length(PpcRegExp(P2)^.RegExp);
//   if l1 > l2 then l:=l2 else
//		     l:=l1;
//   for i:=1 to l do
//     if PpcRegExp(P1).RegExp[i] <> PpcRegExp(P2).RegExp[i] then break;
//   if i <=l then
//     Compare:=ord(PpcRegExp(P1).RegExp[i])-ord(PpcRegExp(P2).RegExp[i]) else
//     Compare:=l1-l2;
    Compare := stringsx.PasStrCmp(PpcRegExp(P1).RegExp, PpcRegExp(P2).RegExp, False);
  end
 else
  begin
//   l1:=length(PpcRegExp(P1)^.RegExp);
//   l2:=length(SearchRegExp);
//   if l1 > l2 then l:=l2 else
//		     l:=l1;
//   for i:=1 to l do
//     if PpcRegExp(P1).RegExp[i] <> SearchRegExp[i] then
//     begin
//       Compare:=ord(PpcRegExp(P1).RegExp[i])-ord(SearchRegExp[i]);
//       break;
//     end;
//   if i > l then Compare:=l1-l2;
    Compare := stringsx.PasStrCmp(PpcRegExp(P1).RegExp, SearchRegExp, False);
  end;
end;

function  TRegExpCollection.Find(ARegExp:shortstring;var P: PpcRegExp):boolean;
var I : integer;
begin
 CompareModeInsert:=false;
 SearchRegExp:=ARegExp;
 if Search(nil,I) then
  begin
   P:=PpcRegExp(At(I));
   Find:=true;
  end
 else
  begin
   P:=nil;
   Find:=false;
  end;
 CompareModeInsert:=true;
end;

function TRegExpCollection.CheckNew(ARegExp:shortstring):PpcRegExp;
var
 P : PpcRegExp;
begin
 if not Find(ARegExp,P) then
  begin
   if Count = MaxRegExp then
    AtFree(0);
   P:=New(ppcRegExp,Init(ARegExp,PCRE_CASELESS,nil));
   Insert(P);
  end;
 CheckNew:=P;
end;

function pcGrepMatch(WildCard, aStr: string; AOptions:integer; ALocale : Pointer): Boolean;
var
 PpcRE:PpcRegExp;
begin
 PpcRE:=New(ppcRegExp,Init(WildCard,AOptions,Alocale));
 pcGrepMatch:=PpcRE^.Search(pchar(AStr),Length(AStr));
 Dispose(PpcRE,Done);
end;

function pcGrepSub(WildCard, aStr, aRepl: string; AOptions:integer; ALocale : Pointer): string;
var
 PpcRE:PpcRegExp;
begin
 PpcRE:=New(ppcRegExp,Init(WildCard,AOptions,Alocale));
 if PpcRE^.Search(pchar(AStr),Length(AStr)) then
  pcGrepSub:=PpcRE^.GetReplStr(pchar(AStr),ARepl)
 else
  pcGrepSub:='';
 Dispose(PpcRE,Done);
end;

function pcFastGrepMatch(WildCard, aStr: string): Boolean;
var
 PpcRE:PpcRegExp;
begin
 PpcRE:=PRegExpCache^.CheckNew(WildCard);
 pcFastGrepMatch:=PpcRE^.Search(pchar(AStr),Length(AStr));
end;

function pcFastGrepSub(WildCard, aStr, aRepl: string): string;
var
 PpcRE:PpcRegExp;
begin
 PpcRE:=PRegExpCache^.CheckNew(WildCard);
 if PpcRE^.Search(pchar(AStr),Length(AStr)) then
  pcFastGrepSub:=PpcRE^.GetReplStr(pchar(AStr),ARepl)
 else
  pcFastGrepSub:='';
end;

{$IFDEF PCRE_5_0}
function pcGetVersion : pchar; assembler; {$FRAME-}{$USES none}
asm
  call pcre_version
end;
{$ENDIF PCRE_5_0}

function pcError;
var P: ppcRegExp absolute pRegExp;
begin
  Result := (P = nil) or P^.Error;
  If Result and (P <> nil) then
  begin
{     if P^.ErrorPos = 0 then
      MessageBox(GetString(erRegExpCompile)+'"'+P^.ErrorStr+'"', nil,mfConfirmation+mfOkButton)
    else}
      MessageBox(GetString(erRegExpCompile)+'"'+P^.ErrorStr+'"'+GetString(erRegExpCompPos),
		 @P^.ErrorPos,mfConfirmation+mfOkButton);
    Dispose(P, Done);
    P:=nil;
  end;
end;

function pcInit;
var Options : Integer;
begin
  If CaseSens then Options := 0 else Options := PCRE_CASELESS;
  Result := New( PpcRegExp, Init( Pattern,
				  {DefaultOptions}
				  startup.MiscMultiData.cfgRegEx.DefaultOptions or Options,
				  DefaultLocaleTable) );
end;

Initialization
 PRegExpCache:=New(PRegExpCollection,Init(64));
Finalization
 Dispose(PRegExpCache,Done);
End.
