Преглед изворни кода

a delphi class for Regular Expression

evan пре 7 година
комит
bd55163098

+ 57 - 0
PerlRegEx.cnt

@@ -0,0 +1,57 @@
+;This help file was created with HelpScribble 7.8.0
+;Licensed to: JGsoft
+
+:BASE PerlRegEx.hlp
+:TITLE TPerlRegEx Help
+:LINK D7VCL.HLP
+1 TPerlRegEx class
+2 TPerlRegEx class Reference=Scribble1010
+2 Properties
+3 Compiled=Scribble1020
+3 FoundMatch=Scribble1025
+3 Match=Scribble1030
+3 MatchLength=Scribble1035
+3 MatchOffset=Scribble1040
+3 Options=Scribble1045
+3 RegEx=Scribble1050
+3 Replacement=Scribble1055
+3 Start=Scribble1060
+3 State=Scribble1065
+3 Stop=Scribble1070
+3 Studied=Scribble1075
+3 GroupCount=Scribble1080
+3 GroupLengths=Scribble1085
+3 GroupOffsets=Scribble1090
+3 Groups=Scribble1095
+3 Subject=Scribble1100
+2 Methods
+3 Compile=Scribble1105
+3 ComputeReplacement=Scribble1110
+3 EscapeRegExChars=Scribble1115
+3 Match=Scribble1120
+3 MatchAgain=Scribble1125
+3 NamedGroup=Scribble1130
+3 Replace=Scribble1135
+3 ReplaceAll=Scribble1140
+3 StoreGroups=Scribble1150
+3 Study=Scribble1155
+2 Events
+3 OnMatch=Scribble1175
+3 OnReplace=Scribble1180
+1 TPerlRegExList class
+2 TPerlRegExList class Reference=Scribble1200
+2 Properties
+3 Count=Scribble1210
+3 MatchedRegEx=Scribble1215
+3 RegEx=Scribble1220
+3 Start=Scribble1225
+3 Stop=Scribble1230
+3 Subject=Scribble1235
+2 Methods
+3 Add=Scribble1245
+3 IndexOf=Scribble1250
+3 Match=Scribble1255
+3 MatchAgain=Scribble1260
+3 Clear=Scribble1265
+3 Delete=Scribble1270
+3 Insert=Scribble1275


+ 963 - 0
PerlRegEx.pas

@@ -0,0 +1,963 @@
+{**************************************************************************************************}
+{                                                                                                  }
+{ Perl Regular Expressions VCL component                                                           }
+{                                                                                                  }
+{ The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); }
+{ you may not use this file except in compliance with the License. You may obtain a copy of the    }
+{ License at http://www.mozilla.org/MPL/                                                           }
+{                                                                                                  }
+{ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF   }
+{ ANY KIND, either express or implied. See the License for the specific language governing rights  }
+{ and limitations under the License.                                                               }
+{                                                                                                  }
+{ The Original Code is PerlRegEx.pas.                                                              }
+{                                                                                                  }
+{ The Initial Developer of the Original Code is Jan Goyvaerts.                                     }
+{ Portions created by Jan Goyvaerts are Copyright (C) 1999, 2005, 2008, 2010  Jan Goyvaerts.       }
+{ All rights reserved.                                                                             }
+{                                                                                                  }
+{ Design & implementation, by Jan Goyvaerts, 1999, 2005, 2008, 2010                                }
+{                                                                                                  }
+{ TPerlRegEx is available at http://www.regular-expressions.info/delphi.html                       }
+{                                                                                                  }
+{**************************************************************************************************}
+
+unit PerlRegEx;
+
+interface
+
+uses
+  Windows, Messages, SysUtils, Classes,
+  pcre;
+
+type
+  TPerlRegExOptions = set of (
+    preCaseLess,       // /i -> Case insensitive
+    preMultiLine,      // /m -> ^ and $ also match before/after a newline, not just at the beginning and the end of the string
+    preSingleLine,     // /s -> Dot matches any character, including \n (newline). Otherwise, it matches anything except \n
+    preExtended,       // /x -> Allow regex to contain extra whitespace, newlines and Perl-style comments, all of which will be filtered out
+    preAnchored,       // /A -> Successful match can only occur at the start of the subject or right after the previous match
+    preUnGreedy,       // Repeat operators (+, *, ?) are not greedy by default (i.e. they try to match the minimum number of characters instead of the maximum)
+    preNoAutoCapture   // (group) is a non-capturing group; only named groups capture
+  );
+
+type
+  TPerlRegExState = set of (
+    preNotBOL,         // Not Beginning Of Line: ^ does not match at the start of Subject
+    preNotEOL,         // Not End Of Line: $ does not match at the end of Subject
+    preNotEmpty        // Empty matches not allowed
+  );
+
+const
+  // Maximum number of subexpressions (backreferences)
+  // Subexpressions are created by placing round brackets in the regex, and are referenced by \1, \2, ...
+  // In Perl, they are available as $1, $2, ... after the regex matched; with TPerlRegEx, use the Subexpressions property
+  // You can also insert \1, \2, ... in the replacement string; \0 is the complete matched expression
+  MAX_SUBEXPRESSIONS = 99;
+
+{$IFDEF UNICODE}
+// All implicit string casts have been verified to be correct
+{$WARN IMPLICIT_STRING_CAST OFF}
+// Use UTF-8 in Delphi 2009 and later, so Unicode strings are handled correctly.
+// PCRE does not support UTF-16
+type
+  PCREString = UTF8String;
+{$ELSE UNICODE}
+// Use AnsiString in Delphi 2007 and earlier
+type
+  PCREString = AnsiString;
+{$ENDIF UNICODE}
+
+type
+  TPerlRegExReplaceEvent = procedure(Sender: TObject; var ReplaceWith: PCREString) of object;
+
+type
+  TPerlRegEx = class
+  private    // *** Property storage, getters and setters
+    FCompiled, FStudied: Boolean;
+    FOptions: TPerlRegExOptions;
+    FState: TPerlRegExState;
+    FRegEx, FReplacement, FSubject: PCREString;
+    FStart, FStop: Integer;
+    FOnMatch: TNotifyEvent;
+    FOnReplace: TPerlRegExReplaceEvent;
+    function GetMatchedText: PCREString;
+    function GetMatchedLength: Integer;
+    function GetMatchedOffset: Integer;
+    procedure SetOptions(Value: TPerlRegExOptions);
+    procedure SetRegEx(const Value: PCREString);
+    function GetGroupCount: Integer;
+    function GetGroups(Index: Integer): PCREString;
+    function GetGroupLengths(Index: Integer): Integer;
+    function GetGroupOffsets(Index: Integer): Integer;
+    procedure SetSubject(const Value: PCREString);
+    procedure SetStart(const Value: Integer);
+    procedure SetStop(const Value: Integer);
+    function GetFoundMatch: Boolean;
+  private    // *** Variables used by PCRE
+    Offsets: array[0..(MAX_SUBEXPRESSIONS+1)*3] of Integer;
+    OffsetCount: Integer;
+    pcreOptions: Integer;
+    pattern, hints, chartable: Pointer;
+    FSubjectPChar: PAnsiChar;
+    FHasStoredGroups: Boolean;
+    FStoredGroups: array of PCREString;
+    function GetSubjectLeft: PCREString;
+    function GetSubjectRight: PCREString;
+  protected
+    procedure CleanUp;
+        // Dispose off whatever we created, so we can start over. Called automatically when needed, so it is not made public
+    procedure ClearStoredGroups;
+  public
+    constructor Create;
+        // Come to life
+    destructor Destroy; override;
+        // Clean up after ourselves
+    class function EscapeRegExChars(const S: string): string;
+        // Escapes regex characters in S so that the regex engine can be used to match S as plain text
+    procedure Compile;
+        // Compile the regex. Called automatically by Match
+    procedure Study;
+        // Study the regex. Studying takes time, but will make the execution of the regex a lot faster.
+        // Call study if you will be using the same regex many times
+    function Match: Boolean;
+        // Attempt to match the regex, starting the attempt from the beginning of Subject
+    function MatchAgain: Boolean;
+        // Attempt to match the regex to the remainder of Subject after the previous match (as indicated by Start)
+    function Replace: PCREString;
+        // Replace matched expression in Subject with ComputeReplacement.  Returns the actual replacement text from ComputeReplacement
+    function ReplaceAll: Boolean;
+        // Repeat MatchAgain and Replace until you drop.  Returns True if anything was replaced at all.
+    function ComputeReplacement: PCREString;
+        // Returns Replacement with backreferences filled in
+    procedure StoreGroups;
+        // Stores duplicates of Groups[] so they and ComputeReplacement will still return the proper strings
+        // even if FSubject is changed or cleared
+    function NamedGroup(const Name: PCREString): Integer;
+        // Returns the index of the named group Name
+    procedure Split(Strings: TStrings; Limit: Integer);
+        // Split Subject along regex matches.  Capturing groups are ignored.
+    procedure SplitCapture(Strings: TStrings; Limit: Integer); overload;
+    procedure SplitCapture(Strings: TStrings; Limit: Integer; Offset: Integer); overload;
+        // Split Subject along regex matches.  Capturing groups are added to Strings as well.
+    property Compiled: Boolean read FCompiled;
+        // True if the RegEx has already been compiled.
+    property FoundMatch: Boolean read GetFoundMatch;
+        // Returns True when Matched* and Group* indicate a match
+    property Studied: Boolean read FStudied;
+        // True if the RegEx has already been studied
+    property MatchedText: PCREString read GetMatchedText;
+        // The matched text
+    property MatchedLength: Integer read GetMatchedLength;
+        // Length of the matched text
+    property MatchedOffset: Integer read GetMatchedOffset;
+        // Character offset in the Subject string at which MatchedText starts
+    property Start: Integer read FStart write SetStart;
+        // Starting position in Subject from which MatchAgain begins
+    property Stop: Integer read FStop write SetStop;
+        // Last character in Subject that Match and MatchAgain search through
+    property State: TPerlRegExState read FState write FState;
+        // State of Subject
+    property GroupCount: Integer read GetGroupCount;
+        // Number of matched capturing groups
+    property Groups[Index: Integer]: PCREString read GetGroups;
+        // Text matched by capturing groups
+    property GroupLengths[Index: Integer]: Integer read GetGroupLengths;
+        // Lengths of the text matched by capturing groups
+    property GroupOffsets[Index: Integer]: Integer read GetGroupOffsets;
+        // Character offsets in Subject at which the capturing group matches start
+    property Subject: PCREString read FSubject write SetSubject;
+        // The string on which Match() will try to match RegEx
+    property SubjectLeft: PCREString read GetSubjectLeft;
+        // Part of the subject to the left of the match
+    property SubjectRight: PCREString read GetSubjectRight;
+        // Part of the subject to the right of the match
+  public
+    property Options: TPerlRegExOptions read FOptions write SetOptions;
+        // Options
+    property RegEx: PCREString read FRegEx write SetRegEx;
+        // The regular expression to be matched
+    property Replacement: PCREString read FReplacement write FReplacement;
+        // Text to replace matched expression with. \number and $number backreferences will be substituted with Groups
+        // TPerlRegEx supports the "JGsoft" replacement text flavor as explained at http://www.regular-expressions.info/refreplace.html
+    property OnMatch: TNotifyEvent read FOnMatch write FOnMatch;
+        // Triggered by Match and MatchAgain after a successful match
+    property OnReplace: TPerlRegExReplaceEvent read FOnReplace write FOnReplace;
+        // Triggered by Replace and ReplaceAll just before the replacement is done, allowing you to determine the new PCREString
+  end;
+
+{
+  You can add TPerlRegEx instances to a TPerlRegExList to match them all together on the same subject,
+  as if they were one regex regex1|regex2|regex3|...
+  TPerlRegExList does not own the TPerlRegEx components, just like a TList
+  If a TPerlRegEx has been added to a TPerlRegExList, it should not be used in any other situation
+  until it is removed from the list
+}
+
+type
+  TPerlRegExList = class
+  private
+    FList: TList;
+    FSubject: PCREString;
+    FMatchedRegEx: TPerlRegEx;
+    FStart, FStop: Integer;
+    function GetRegEx(Index: Integer): TPerlRegEx;
+    procedure SetRegEx(Index: Integer; Value: TPerlRegEx);
+    procedure SetSubject(const Value: PCREString);
+    procedure SetStart(const Value: Integer);
+    procedure SetStop(const Value: Integer);
+    function GetCount: Integer;
+  protected
+    procedure UpdateRegEx(ARegEx: TPerlRegEx);
+  public
+    constructor Create;
+    destructor Destroy; override;
+  public
+    function Add(ARegEx: TPerlRegEx): Integer;
+    procedure Clear;
+    procedure Delete(Index: Integer);
+    function IndexOf(ARegEx: TPerlRegEx): Integer;
+    procedure Insert(Index: Integer; ARegEx: TPerlRegEx);
+  public
+    function Match: Boolean;
+    function MatchAgain: Boolean;
+    property RegEx[Index: Integer]: TPerlRegEx read GetRegEx write SetRegEx;
+    property Count: Integer read GetCount;
+    property Subject: PCREString read FSubject write SetSubject;
+    property Start: Integer read FStart write SetStart;
+    property Stop: Integer read FStop write SetStop;
+    property MatchedRegEx: TPerlRegEx read FMatchedRegEx;
+  end;
+
+implementation
+
+
+         { ********* Unit support routines ********* }
+
+function FirstCap(const S: string): string;
+begin
+  if S = '' then Result := ''
+  else begin
+    Result := AnsiLowerCase(S);
+  {$IFDEF UNICODE}
+    CharUpperBuffW(@Result[1], 1);
+  {$ELSE}
+    CharUpperBuffA(@Result[1], 1);
+  {$ENDIF}
+  end
+end;
+
+function InitialCaps(const S: string): string;
+var
+  I: Integer;
+  Up: Boolean;
+begin
+  Result := AnsiLowerCase(S);
+  Up := True;
+{$IFDEF UNICODE}
+  for I := 1 to Length(Result) do begin
+    case Result[I] of
+      #0..'&', '(', '*', '+', ',', '-', '.', '?', '<', '[', '{', #$00B7:
+        Up := True
+      else
+        if Up and (Result[I] <> '''') then begin
+          CharUpperBuffW(@Result[I], 1);
+          Up := False
+        end
+    end;
+  end;
+{$ELSE UNICODE}
+  if SysLocale.FarEast then begin
+    I := 1;
+    while I <= Length(Result) do begin
+      if Result[I] in LeadBytes then begin
+        Inc(I, 2)
+      end
+      else begin
+        if Result[I] in [#0..'&', '('..'.', '?', '<', '[', '{'] then Up := True
+        else if Up and (Result[I] <> '''') then begin
+          CharUpperBuffA(@Result[I], 1);
+          Result[I] := UpperCase(Result[I])[1];
+          Up := False
+        end;
+        Inc(I)
+      end
+    end
+  end
+  else
+    for I := 1 to Length(Result) do begin
+      if Result[I] in [#0..'&', '('..'.', '?', '<', '[', '{', #$B7] then Up := True
+      else if Up and (Result[I] <> '''') then begin
+        CharUpperBuffA(@Result[I], 1);
+        Result[I] := AnsiUpperCase(Result[I])[1];
+        Up := False
+      end
+    end;
+{$ENDIF UNICODE}
+end;
+
+
+         { ********* TPerlRegEx component ********* }
+
+procedure TPerlRegEx.CleanUp;
+begin
+  FCompiled := False; FStudied := False;
+  pcre_dispose(pattern, hints, nil);
+  pattern := nil;
+  hints := nil;
+  ClearStoredGroups;
+  OffsetCount := 0;
+end;
+
+procedure TPerlRegEx.ClearStoredGroups;
+begin
+  FHasStoredGroups := False;
+  FStoredGroups := nil;
+end;
+
+procedure TPerlRegEx.Compile;
+var
+  Error: PAnsiChar;
+  ErrorOffset: Integer;
+begin
+  if FRegEx = '' then
+    raise Exception.Create('TPerlRegEx.Compile() - Please specify a regular expression in RegEx first');
+  CleanUp;
+  Pattern := pcre_compile(PAnsiChar(FRegEx), pcreOptions, @Error, @ErrorOffset, chartable);
+  if Pattern = nil then
+    raise Exception.Create(Format('TPerlRegEx.Compile() - Error in regex at offset %d: %s', [ErrorOffset, AnsiString(Error)]));
+  FCompiled := True
+end;
+
+(* Backreference overview:
+
+Assume there are 13 backreferences:
+
+Text        TPerlRegex    .NET      Java       ECMAScript
+$17         $1 + "7"      "$17"     $1 + "7"   $1 + "7"
+$017        $1 + "7"      "$017"    $1 + "7"   $1 + "7"
+$12         $12           $12       $12        $12
+$012        $1 + "2"      $12       $12        $1 + "2"
+${1}2       $1 + "2"      $1 + "2"  error      "${1}2"
+$$          "$"           "$"       error      "$"
+\$          "$"           "\$"      "$"        "\$"
+*)
+
+function TPerlRegEx.ComputeReplacement: PCREString;
+var
+  Mode: AnsiChar;
+  S: PCREString;
+  I, J, N: Integer;
+
+  procedure ReplaceBackreference(Number: Integer);
+  var
+    Backreference: PCREString;
+  begin
+    Delete(S, I, J-I);
+    if Number <= GroupCount then begin
+      Backreference := Groups[Number];
+      if Backreference <> '' then begin
+        // Ignore warnings; converting to UTF-8 does not cause data loss
+        case Mode of
+          'L', 'l': Backreference := AnsiLowerCase(Backreference);
+          'U', 'u': Backreference := AnsiUpperCase(Backreference);
+          'F', 'f': Backreference := FirstCap(Backreference);
+          'I', 'i': Backreference := InitialCaps(Backreference);
+        end;
+        if S <> '' then begin
+          Insert(Backreference, S, I);
+          I := I + Length(Backreference);
+        end
+        else begin
+          S := Backreference;
+          I := MaxInt;
+        end
+      end;
+    end
+  end;
+
+  procedure ProcessBackreference(NumberOnly, Dollar: Boolean);
+  var
+    Number, Number2: Integer;
+    Group: PCREString;
+  begin
+    Number := -1;
+    if (J <= Length(S)) and (S[J] in ['0'..'9']) then begin
+      // Get the number of the backreference
+      Number := Ord(S[J]) - Ord('0');
+      Inc(J);
+      if (J <= Length(S)) and (S[J] in ['0'..'9']) then begin
+        // Expand it to two digits only if that would lead to a valid backreference
+        Number2 := Number*10 + Ord(S[J]) - Ord('0');
+        if Number2 <= GroupCount then begin
+          Number := Number2;
+          Inc(J)
+        end;
+      end;
+    end
+    else if not NumberOnly then begin
+      if Dollar and (J < Length(S)) and (S[J] = '{') then begin
+        // Number or name in curly braces
+        Inc(J);
+        case S[J] of
+          '0'..'9': begin
+            Number := Ord(S[J]) - Ord('0');
+            Inc(J);
+            while (J <= Length(S)) and (S[J] in ['0'..'9']) do begin
+              Number := Number*10 + Ord(S[J]) - Ord('0');
+              Inc(J)
+            end;
+          end;
+          'A'..'Z', 'a'..'z', '_': begin
+            Inc(J);
+            while (J <= Length(S)) and (S[J] in ['A'..'Z', 'a'..'z', '0'..'9', '_']) do Inc(J);
+            if (J <= Length(S)) and (S[J] = '}') then begin
+              Group := Copy(S, I+2, J-I-2);
+              Number := NamedGroup(Group);
+            end
+          end;
+        end;
+        if (J > Length(S)) or (S[J] <> '}') then Number := -1
+          else Inc(J)
+      end
+      else if Dollar and (S[J] = '_') then begin
+        // $_ (whole subject)
+        Delete(S, I, J+1-I);
+        Insert(Subject, S, I);
+        I := I + Length(Subject);
+        Exit;
+      end
+      else case S[J] of
+        '&': begin
+          // \& or $& (whole regex match)
+          Number := 0;
+          Inc(J);
+        end;
+        '+': begin
+          // \+ or $+ (highest-numbered participating group)
+          Number := GroupCount;
+          Inc(J);
+        end;
+        '`': begin
+          // \` or $` (backtick; subject to the left of the match)
+          Delete(S, I, J+1-I);
+          Insert(SubjectLeft, S, I);
+          I := I + Offsets[0] - 1;
+          Exit;
+        end;
+        '''': begin
+          // \' or $' (straight quote; subject to the right of the match)
+          Delete(S, I, J+1-I);
+          Insert(SubjectRight, S, I);
+          I := I + Length(Subject) - Offsets[1];
+          Exit;
+        end
+      end;
+    end;
+    if Number >= 0 then ReplaceBackreference(Number)
+      else Inc(I)
+  end;
+
+begin
+  S := FReplacement;
+  I := 1;
+  while I < Length(S) do begin
+    case S[I] of
+      '\': begin
+        J := I + 1;
+        Assert(J <= Length(S), 'CHECK: We let I stop one character before the end, so J cannot point beyond the end of the PCREString here');
+        case S[J] of
+          '$', '\': begin
+            Delete(S, I, 1);
+            Inc(I);
+          end;
+          'g': begin
+            if (J < Length(S)-1) and (S[J+1] = '<') and (S[J+2] in ['A'..'Z', 'a'..'z', '_']) then begin
+              // Python-style named group reference \g<name>
+              J := J+3;
+              while (J <= Length(S)) and (S[J] in ['0'..'9', 'A'..'Z', 'a'..'z', '_']) do Inc(J);
+              if (J <= Length(S)) and (S[J] = '>') then begin
+                N := NamedGroup(Copy(S, I+3, J-I-3));
+                Inc(J);
+                Mode := #0;
+                if N > 0 then ReplaceBackreference(N)
+                  else Delete(S, I, J-I)
+              end
+              else I := J
+            end
+            else I := I+2;
+          end;
+          'l', 'L', 'u', 'U', 'f', 'F', 'i', 'I': begin
+            Mode := S[J];
+            Inc(J);
+            ProcessBackreference(True, False);
+          end;
+          else begin
+            Mode := #0;
+            ProcessBackreference(False, False);
+          end;
+        end;
+      end;
+      '$': begin
+        J := I + 1;
+        Assert(J <= Length(S), 'CHECK: We let I stop one character before the end, so J cannot point beyond the end of the PCREString here');
+        if S[J] = '$' then begin
+          Delete(S, J, 1);
+          Inc(I);
+        end
+        else begin
+          Mode := #0;
+          ProcessBackreference(False, True);
+        end
+      end;
+      else Inc(I)
+    end
+  end;
+  Result := S
+end;
+
+constructor TPerlRegEx.Create;
+begin
+  inherited Create;
+  FState := [preNotEmpty];
+  chartable := pcre_maketables;
+{$IFDEF UNICODE}
+  pcreOptions := PCRE_UTF8 or PCRE_NEWLINE_ANY;
+{$ELSE}
+  pcreOptions := PCRE_NEWLINE_ANY;
+{$ENDIF}
+end;
+
+destructor TPerlRegEx.Destroy;
+begin
+  pcre_dispose(pattern, hints, chartable);
+  inherited Destroy;
+end;
+
+class function TPerlRegEx.EscapeRegExChars(const S: string): string;
+var
+  I: Integer;
+begin
+  Result := S;
+  I := Length(Result);
+  while I > 0 do begin
+    case Result[I] of
+      '.', '[', ']', '(', ')', '?', '*', '+', '{', '}', '^', '$', '|', '\':
+        Insert('\', Result, I);
+      #0: begin
+        Result[I] := '0';
+        Insert('\', Result, I);
+      end;
+    end;
+    Dec(I);
+  end;
+end;
+
+function TPerlRegEx.GetFoundMatch: Boolean;
+begin
+  Result := OffsetCount > 0;
+end;
+
+function TPerlRegEx.GetMatchedText: PCREString;
+begin
+  Assert(FoundMatch, 'REQUIRE: There must be a successful match first');
+  Result := GetGroups(0);
+end;
+
+function TPerlRegEx.GetMatchedLength: Integer;
+begin
+  Assert(FoundMatch, 'REQUIRE: There must be a successful match first');
+  Result := GetGroupLengths(0)
+end;
+
+function TPerlRegEx.GetMatchedOffset: Integer;
+begin
+  Assert(FoundMatch, 'REQUIRE: There must be a successful match first');
+  Result := GetGroupOffsets(0)
+end;
+
+function TPerlRegEx.GetGroupCount: Integer;
+begin
+  Assert(FoundMatch, 'REQUIRE: There must be a successful match first');
+  Result := OffsetCount-1
+end;
+
+function TPerlRegEx.GetGroupLengths(Index: Integer): Integer;
+begin
+  Assert(FoundMatch, 'REQUIRE: There must be a successful match first');
+  Assert((Index >= 0) and (Index <= GroupCount), 'REQUIRE: Index <= GroupCount');
+  Result := Offsets[Index*2+1]-Offsets[Index*2]
+end;
+
+function TPerlRegEx.GetGroupOffsets(Index: Integer): Integer;
+begin
+  Assert(FoundMatch, 'REQUIRE: There must be a successful match first');
+  Assert((Index >= 0) and (Index <= GroupCount), 'REQUIRE: Index <= GroupCount');
+  Result := Offsets[Index*2]
+end;
+
+function TPerlRegEx.GetGroups(Index: Integer): PCREString;
+begin
+  Assert(FoundMatch, 'REQUIRE: There must be a successful match first');
+  if Index > GroupCount then Result := ''
+    else if FHasStoredGroups then Result := FStoredGroups[Index]
+    else Result := Copy(FSubject, Offsets[Index*2], Offsets[Index*2+1]-Offsets[Index*2]);
+end;
+
+function TPerlRegEx.GetSubjectLeft: PCREString;
+begin
+  Result := Copy(Subject, 1, Offsets[0]-1);
+end;
+
+function TPerlRegEx.GetSubjectRight: PCREString;
+begin
+  Result := Copy(Subject, Offsets[1], MaxInt);
+end;
+
+function TPerlRegEx.Match: Boolean;
+var
+  I, Opts: Integer;
+begin
+  ClearStoredGroups;
+  if not Compiled then Compile;
+  if preNotBOL in State then Opts := PCRE_NOTBOL else Opts := 0;
+  if preNotEOL in State then Opts := Opts or PCRE_NOTEOL;
+  if preNotEmpty in State then Opts := Opts or PCRE_NOTEMPTY;
+  OffsetCount := pcre_exec(Pattern, Hints, FSubjectPChar, FStop, 0, Opts, @Offsets[0], High(Offsets));
+  Result := OffsetCount > 0;
+  // Convert offsets into PCREString indices
+  if Result then begin
+    for I := 0 to OffsetCount*2-1 do
+      Inc(Offsets[I]);
+    FStart := Offsets[1];
+    if Offsets[0] = Offsets[1] then Inc(FStart); // Make sure we don't get stuck at the same position
+    if Assigned(OnMatch) then OnMatch(Self)
+  end;
+end;
+
+function TPerlRegEx.MatchAgain: Boolean;
+var
+  I, Opts: Integer;
+begin
+  ClearStoredGroups;
+  if not Compiled then Compile;
+  if preNotBOL in State then Opts := PCRE_NOTBOL else Opts := 0;
+  if preNotEOL in State then Opts := Opts or PCRE_NOTEOL;
+  if preNotEmpty in State then Opts := Opts or PCRE_NOTEMPTY;
+  if FStart-1 > FStop then OffsetCount := -1
+    else OffsetCount := pcre_exec(Pattern, Hints, FSubjectPChar, FStop, FStart-1, Opts, @Offsets[0], High(Offsets));
+  Result := OffsetCount > 0;
+  // Convert offsets into PCREString indices
+  if Result then begin
+    for I := 0 to OffsetCount*2-1 do
+      Inc(Offsets[I]);
+    FStart := Offsets[1];
+    if Offsets[0] = Offsets[1] then Inc(FStart); // Make sure we don't get stuck at the same position
+    if Assigned(OnMatch) then OnMatch(Self)
+  end;
+end;
+
+function TPerlRegEx.NamedGroup(const Name: PCREString): Integer;
+begin
+  Result := pcre_get_stringnumber(Pattern, PAnsiChar(Name));
+end;
+
+function TPerlRegEx.Replace: PCREString;
+begin
+  Assert(FoundMatch, 'REQUIRE: There must be a successful match first');
+  // Substitute backreferences
+  Result := ComputeReplacement;
+  // Allow for just-in-time substitution determination
+  if Assigned(OnReplace) then OnReplace(Self, Result);
+  // Perform substitution
+  Delete(FSubject, MatchedOffset, MatchedLength);
+  if Result <> '' then Insert(Result, FSubject, MatchedOffset);
+  FSubjectPChar := PAnsiChar(FSubject);
+  // Position to continue search
+  FStart := FStart - MatchedLength + Length(Result);
+  FStop := FStop - MatchedLength + Length(Result);
+  // Replacement no longer matches regex, we assume
+  ClearStoredGroups;
+  OffsetCount := 0;
+end;
+
+function TPerlRegEx.ReplaceAll: Boolean;
+begin
+  if Match then begin
+    Result := True;
+    repeat
+      Replace
+    until not MatchAgain;
+  end
+  else Result := False;
+end;
+
+procedure TPerlRegEx.SetOptions(Value: TPerlRegExOptions);
+begin
+  if (FOptions <> Value) then begin
+    FOptions := Value;
+  {$IFDEF UNICODE}
+    pcreOptions := PCRE_UTF8 or PCRE_NEWLINE_ANY;
+  {$ELSE}
+    pcreOptions := PCRE_NEWLINE_ANY;
+  {$ENDIF}
+    if (preCaseLess in Value) then pcreOptions := pcreOptions or PCRE_CASELESS;
+    if (preMultiLine in Value) then pcreOptions := pcreOptions or PCRE_MULTILINE;
+    if (preSingleLine in Value) then pcreOptions := pcreOptions or PCRE_DOTALL;
+    if (preExtended in Value) then pcreOptions := pcreOptions or PCRE_EXTENDED;
+    if (preAnchored in Value) then pcreOptions := pcreOptions or PCRE_ANCHORED;
+    if (preUnGreedy in Value) then pcreOptions := pcreOptions or PCRE_UNGREEDY;
+    if (preNoAutoCapture in Value) then pcreOptions := pcreOptions or PCRE_NO_AUTO_CAPTURE;
+    CleanUp
+  end
+end;
+
+procedure TPerlRegEx.SetRegEx(const Value: PCREString);
+begin
+  if FRegEx <> Value then begin
+    FRegEx := Value;
+    CleanUp
+  end
+end;
+
+procedure TPerlRegEx.SetStart(const Value: Integer);
+begin
+  if Value < 1 then FStart := 1
+  else FStart := Value;
+  // If FStart > Length(Subject), MatchAgain() will simply return False
+end;
+
+procedure TPerlRegEx.SetStop(const Value: Integer);
+begin
+  if Value > Length(Subject) then FStop := Length(Subject)
+    else FStop := Value;
+end;
+
+procedure TPerlRegEx.SetSubject(const Value: PCREString);
+begin
+  FSubject := Value;
+  FSubjectPChar := PAnsiChar(Value);
+  FStart := 1;
+  FStop := Length(Subject);
+  if not FHasStoredGroups then OffsetCount := 0;
+end;
+
+procedure TPerlRegEx.Split(Strings: TStrings; Limit: Integer);
+var
+  Offset, Count: Integer;
+begin
+  Assert(Strings <> nil, 'REQUIRE: Strings');
+  if (Limit = 1) or not Match then Strings.Add(Subject)
+  else begin
+    Offset := 1;
+    Count := 1;
+    repeat
+      Strings.Add(Copy(Subject, Offset, MatchedOffset - Offset));
+      Inc(Count);
+      Offset := MatchedOffset + MatchedLength;
+    until ((Limit > 1) and (Count >= Limit)) or not MatchAgain;
+    Strings.Add(Copy(Subject, Offset, MaxInt));
+  end
+end;
+
+procedure TPerlRegEx.SplitCapture(Strings: TStrings; Limit, Offset: Integer);
+var
+  Count: Integer;
+  bUseOffset : boolean;
+  iOffset : integer;
+begin
+  Assert(Strings <> nil, 'REQUIRE: Strings');
+  if (Limit = 1) or not Match then Strings.Add(Subject)
+  else
+  begin
+    bUseOffset := Offset <> 1;
+    if Offset <> 1 then
+      Dec(Limit);
+    iOffset := 1;
+    Count := 1;
+    repeat
+      if bUseOffset then
+      begin
+        if MatchedOffset >= Offset then
+        begin
+          bUseOffset := False;
+          Strings.Add(Copy(Subject, 1, MatchedOffset -1));
+          if Self.GroupCount > 0 then
+            Strings.Add(Self.Groups[Self.GroupCount]);
+        end;
+      end
+      else
+      begin
+        Strings.Add(Copy(Subject, iOffset, MatchedOffset - iOffset));
+        Inc(Count);
+        if Self.GroupCount > 0 then
+          Strings.Add(Self.Groups[Self.GroupCount]);
+      end;
+      iOffset := MatchedOffset + MatchedLength;
+    until ((Limit > 1) and (Count >= Limit)) or not MatchAgain;
+    Strings.Add(Copy(Subject, iOffset, MaxInt));
+  end
+end;
+
+procedure TPerlRegEx.SplitCapture(Strings: TStrings; Limit: Integer);
+begin
+  SplitCapture(Strings,Limit,1);
+end;
+
+procedure TPerlRegEx.StoreGroups;
+var
+  I: Integer;
+begin
+  if OffsetCount > 0 then begin
+    ClearStoredGroups;
+    SetLength(FStoredGroups, GroupCount+1);
+    for I := GroupCount downto 0 do
+      FStoredGroups[I] := Groups[I];
+    FHasStoredGroups := True;
+  end
+end;
+
+procedure TPerlRegEx.Study;
+var
+  Error: PAnsiChar;
+begin
+  if not FCompiled then Compile;
+  Hints := pcre_study(Pattern, 0, @Error);
+  if Error <> nil then
+    raise Exception.Create('TPerlRegEx.Study() - Error studying the regex: ' + AnsiString(Error));
+  FStudied := True
+end;
+
+{ TPerlRegExList }
+
+function TPerlRegExList.Add(ARegEx: TPerlRegEx): Integer;
+begin
+  Result := FList.Add(ARegEx);
+  UpdateRegEx(ARegEx);
+end;
+
+procedure TPerlRegExList.Clear;
+begin
+  FList.Clear;
+end;
+
+constructor TPerlRegExList.Create;
+begin
+  inherited Create;
+  FList := TList.Create;
+end;
+
+procedure TPerlRegExList.Delete(Index: Integer);
+begin
+  FList.Delete(Index);
+end;
+
+destructor TPerlRegExList.Destroy;
+begin
+  FList.Free;
+  inherited
+end;
+
+function TPerlRegExList.GetCount: Integer;
+begin
+  Result := FList.Count;
+end;
+
+function TPerlRegExList.GetRegEx(Index: Integer): TPerlRegEx;
+begin
+  Result := TPerlRegEx(Pointer(FList[Index]));
+end;
+
+function TPerlRegExList.IndexOf(ARegEx: TPerlRegEx): Integer;
+begin
+  Result := FList.IndexOf(ARegEx);
+end;
+
+procedure TPerlRegExList.Insert(Index: Integer; ARegEx: TPerlRegEx);
+begin
+  FList.Insert(Index, ARegEx);
+  UpdateRegEx(ARegEx);
+end;
+
+function TPerlRegExList.Match: Boolean;
+begin
+  SetStart(1);
+  FMatchedRegEx := nil;
+  Result := MatchAgain;
+end;
+
+function TPerlRegExList.MatchAgain: Boolean;
+var
+  I, MatchStart, MatchPos: Integer;
+  ARegEx: TPerlRegEx;
+begin
+  if FMatchedRegEx <> nil then
+    MatchStart := FMatchedRegEx.MatchedOffset + FMatchedRegEx.MatchedLength
+  else
+    MatchStart := FStart;
+  FMatchedRegEx := nil;
+  MatchPos := MaxInt;
+  for I := 0 to Count-1 do begin
+    ARegEx := RegEx[I];
+    if (not ARegEx.FoundMatch) or (ARegEx.MatchedOffset < MatchStart) then begin
+      ARegEx.Start := MatchStart;
+      ARegEx.MatchAgain;
+    end;
+    if ARegEx.FoundMatch and (ARegEx.MatchedOffset < MatchPos) then begin
+      MatchPos := ARegEx.MatchedOffset;
+      FMatchedRegEx := ARegEx;
+    end;
+    if MatchPos = MatchStart then Break;
+  end;
+  Result := MatchPos < MaxInt;
+end;
+
+procedure TPerlRegExList.SetRegEx(Index: Integer; Value: TPerlRegEx);
+begin
+  FList[Index] := Value;
+  UpdateRegEx(Value);
+end;
+
+procedure TPerlRegExList.SetStart(const Value: Integer);
+var
+  I: Integer;
+begin
+  if FStart <> Value then begin
+    FStart := Value;
+    for I := Count-1 downto 0 do
+      RegEx[I].Start := Value;
+    FMatchedRegEx := nil;
+  end;
+end;
+
+procedure TPerlRegExList.SetStop(const Value: Integer);
+var
+  I: Integer;
+begin
+  if FStop <> Value then begin
+    FStop := Value;
+    for I := Count-1 downto 0 do
+      RegEx[I].Stop := Value;
+    FMatchedRegEx := nil;
+  end;
+end;
+
+procedure TPerlRegExList.SetSubject(const Value: PCREString);
+var
+  I: Integer;
+begin
+  if FSubject <> Value then begin
+    FSubject := Value;
+    for I := Count-1 downto 0 do
+      RegEx[I].Subject := Value;
+    FMatchedRegEx := nil;
+  end;
+end;
+
+procedure TPerlRegExList.UpdateRegEx(ARegEx: TPerlRegEx);
+begin
+  ARegEx.Subject := FSubject;
+  ARegEx.Start := FStart;
+end;
+
+end.

+ 15 - 0
README.txt

@@ -0,0 +1,15 @@
+TPerlRegEx is a Delphi VCL wrapper around the open source PCRE library, which implements Perl-Compatible Regular Expressions.
+
+This version of TPerlRegEx is compatible with the TPerlRegEx class in the RegularExpressionsCore unit in Delphi XE.  In fact, the unit in Delphi XE is derived from the version of TPerlRegEx that you are using now.
+
+The supplied pcrelib.dll contains PCRE 7.9, compiled with Unicode support.  The supplied OBJ files contain PCRE 7.9, compiled with Unicode support.  By default, the OBJ files are used.  You can use the DLL if you have multiple applications using TPerlRegEx and you don't want to waste space by linking the OBJ files to be linked into each of those applications.
+
+For more information about PCRE, please visit http://www.regular-expressions.info/pcre.html
+
+For more information about regular expressions in general, please visit http://www.regular-expressions.info/
+
+You can download the latest version of TPerlRegEx at http://www.regular-expressions.info/delphi.html
+
+TPerlRegEx is licensed under the Mozilla Public License, version 1.1.
+
+This new version of TPerlRegEx descends from TObject.  There are no packages to install into Delphi and nothing appears on the component palette.  Simply add PerlRegEx to the uses clause of any units you want to use it in.  There's no need to add the pcre unit to the uses clause.  This unit is used internally by TPerlRegEx.

+ 1145 - 0
pcre.pas

@@ -0,0 +1,1145 @@
+{**************************************************************************************************}
+{                                                                                                  }
+{ Project JEDI Code Library (JCL)                                                                  }
+{                                                                                                  }
+{ The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); }
+{ you may not use this file except in compliance with the License. You may obtain a copy of the    }
+{ License at http://www.mozilla.org/MPL/                                                           }
+{                                                                                                  }
+{ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF   }
+{ ANY KIND, either express or implied. See the License for the specific language governing rights  }
+{ and limitations under the License.                                                               }
+{                                                                                                  }
+{ The Original Code is pcre.pas.                                                                   }
+{                                                                                                  }
+{ The Initial Developer of the Original Code is Peter Thornqvist.                                  }
+{ Portions created by Peter Thornqvist are Copyright (C) of Peter Thornqvist. All rights reserved. }
+{ Portions created by University of Cambridge are                                                  }
+{ Copyright (C) 1997-2001 by University of Cambridge.                                              }
+{                                                                                                  }
+{ Contributor(s):                                                                                  }
+{   Robert Rossmair (rrossmair)                                                                    }
+{   Mario R. Carro                                                                                 }
+{   Florent Ouchet (outchy)                                                                        }
+{                                                                                                  }
+{ The latest release of PCRE is always available from                                              }
+{ ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-xxx.tar.gz                            }
+{                                                                                                  }
+{ Modified by Jan Goyvaerts for use with TPerlRegEx                                                }
+{ TPerlRegEx is available at http://www.regular-expressions.info/delphi.html                       }
+{                                                                                                  }
+{**************************************************************************************************}
+{                                                                                                  }
+{ Header conversion of pcre.h                                                                      }
+{                                                                                                  }
+{**************************************************************************************************}
+
+unit pcre;
+
+interface
+
+(*************************************************
+*       Perl-Compatible Regular Expressions      *
+*************************************************)
+
+{$WEAKPACKAGEUNIT ON}
+
+// Define PCRE_STATICLINK to link the OBJ files with PCRE 7.9.
+{$DEFINE PCRE_STATICLINK}
+
+// Define PCRE_LINKDLL to use pcrelib.dll
+{.$DEFINE PCRE_LINKDLL}
+
+// The supplied pcrelib.dll compiled PCRE 7.9 using the C calling convention
+{$IFDEF PCRE_LINKDLL}
+  {$DEFINE PCRE_EXPORT_CDECL}
+{$ENDIF}
+
+(*$HPPEMIT '#include "pcre.h"'*)
+
+const
+  MAX_PATTERN_LENGTH = $10003;
+  {$EXTERNALSYM MAX_PATTERN_LENGTH}
+  MAX_QUANTIFY_REPEAT = $10000;
+  {$EXTERNALSYM MAX_QUANTIFY_REPEAT}
+  MAX_CAPTURE_COUNT = $FFFF;
+  {$EXTERNALSYM MAX_CAPTURE_COUNT}
+  MAX_NESTING_DEPTH = 200;
+  {$EXTERNALSYM MAX_NESTING_DEPTH}
+
+const
+  (* Options *)
+  PCRE_CASELESS = $00000001;
+  {$EXTERNALSYM PCRE_CASELESS}
+  PCRE_MULTILINE = $00000002;
+  {$EXTERNALSYM PCRE_MULTILINE}
+  PCRE_DOTALL = $00000004;
+  {$EXTERNALSYM PCRE_DOTALL}
+  PCRE_EXTENDED = $00000008;
+  {$EXTERNALSYM PCRE_EXTENDED}
+  PCRE_ANCHORED = $00000010;
+  {$EXTERNALSYM PCRE_ANCHORED}
+  PCRE_DOLLAR_ENDONLY = $00000020;
+  {$EXTERNALSYM PCRE_DOLLAR_ENDONLY}
+  PCRE_EXTRA = $00000040;
+  {$EXTERNALSYM PCRE_EXTRA}
+  PCRE_NOTBOL = $00000080;
+  {$EXTERNALSYM PCRE_NOTBOL}
+  PCRE_NOTEOL = $00000100;
+  {$EXTERNALSYM PCRE_NOTEOL}
+  PCRE_UNGREEDY = $00000200;
+  {$EXTERNALSYM PCRE_UNGREEDY}
+  PCRE_NOTEMPTY = $00000400;
+  {$EXTERNALSYM PCRE_NOTEMPTY}
+  PCRE_UTF8 = $00000800;
+  {$EXTERNALSYM PCRE_UTF8}
+  PCRE_NO_AUTO_CAPTURE = $00001000;
+  {$EXTERNALSYM PCRE_NO_AUTO_CAPTURE}
+  PCRE_NO_UTF8_CHECK = $00002000;
+  {$EXTERNALSYM PCRE_NO_UTF8_CHECK}
+  PCRE_AUTO_CALLOUT = $00004000;
+  {$EXTERNALSYM PCRE_AUTO_CALLOUT}
+  PCRE_PARTIAL = $00008000;
+  {$EXTERNALSYM PCRE_PARTIAL}
+  PCRE_DFA_SHORTEST = $00010000;
+  {$EXTERNALSYM PCRE_DFA_SHORTEST}
+  PCRE_DFA_RESTART = $00020000;
+  {$EXTERNALSYM PCRE_DFA_RESTART}
+  PCRE_FIRSTLINE = $00040000;
+  {$EXTERNALSYM PCRE_FIRSTLINE}
+  PCRE_DUPNAMES = $00080000;
+  {$EXTERNALSYM PCRE_DUPNAMES}
+  PCRE_NEWLINE_CR = $00100000;
+  {$EXTERNALSYM PCRE_NEWLINE_CR}
+  PCRE_NEWLINE_LF = $00200000;
+  {$EXTERNALSYM PCRE_NEWLINE_LF}
+  PCRE_NEWLINE_CRLF = $00300000;
+  {$EXTERNALSYM PCRE_NEWLINE_CRLF}
+  PCRE_NEWLINE_ANY = $00400000;
+  {$EXTERNALSYM PCRE_NEWLINE_ANY}
+  PCRE_NEWLINE_ANYCRLF = $00500000;
+  {$EXTERNALSYM PCRE_NEWLINE_ANYCRLF}
+  PCRE_BSR_ANYCRLF = $00800000;
+  {$EXTERNALSYM PCRE_BSR_ANYCRLF}
+  PCRE_BSR_UNICODE = $01000000;
+  {$EXTERNALSYM PCRE_BSR_UNICODE}
+  PCRE_JAVASCRIPT_COMPAT = $02000000;
+  {$EXTERNALSYM PCRE_JAVASCRIPT_COMPAT}
+  PCRE_NO_START_OPTIMIZE = $04000000;
+  {$EXTERNALSYM PCRE_NO_START_OPTIMIZE}
+  PCRE_NO_START_OPTIMISE = $04000000;
+  {$EXTERNALSYM PCRE_NO_START_OPTIMISE}
+
+  (* Exec-time and get-time error codes *)
+
+  PCRE_ERROR_NOMATCH = -1;
+  {$EXTERNALSYM PCRE_ERROR_NOMATCH}
+  PCRE_ERROR_NULL = -2;
+  {$EXTERNALSYM PCRE_ERROR_NULL}
+  PCRE_ERROR_BADOPTION = -3;
+  {$EXTERNALSYM PCRE_ERROR_BADOPTION}
+  PCRE_ERROR_BADMAGIC = -4;
+  {$EXTERNALSYM PCRE_ERROR_BADMAGIC}
+  PCRE_ERROR_UNKNOWN_NODE = -5;
+  {$EXTERNALSYM PCRE_ERROR_UNKNOWN_NODE}
+  PCRE_ERROR_NOMEMORY = -6;
+  {$EXTERNALSYM PCRE_ERROR_NOMEMORY}
+  PCRE_ERROR_NOSUBSTRING = -7;
+  {$EXTERNALSYM PCRE_ERROR_NOSUBSTRING}
+  PCRE_ERROR_MATCHLIMIT = -8;
+  {$EXTERNALSYM PCRE_ERROR_MATCHLIMIT}
+  PCRE_ERROR_CALLOUT = -9;  (* Never used by PCRE itself *)
+  {$EXTERNALSYM PCRE_ERROR_CALLOUT}
+  PCRE_ERROR_BADUTF8 = -10;
+  {$EXTERNALSYM PCRE_ERROR_BADUTF8}
+  PCRE_ERROR_BADUTF8_OFFSET = -11;
+  {$EXTERNALSYM PCRE_ERROR_BADUTF8_OFFSET}
+  PCRE_ERROR_PARTIAL = -12;
+  {$EXTERNALSYM PCRE_ERROR_PARTIAL}
+  PCRE_ERROR_BADPARTIAL = -13;
+  {$EXTERNALSYM PCRE_ERROR_BADPARTIAL}
+  PCRE_ERROR_INTERNAL = -14;
+  {$EXTERNALSYM PCRE_ERROR_INTERNAL}
+  PCRE_ERROR_BADCOUNT = -15;
+  {$EXTERNALSYM PCRE_ERROR_BADCOUNT}
+  PCRE_ERROR_DFA_UITEM = -16;
+  {$EXTERNALSYM PCRE_ERROR_DFA_UITEM}
+  PCRE_ERROR_DFA_UCOND = -17;
+  {$EXTERNALSYM PCRE_ERROR_DFA_UCOND}
+  PCRE_ERROR_DFA_UMLIMIT = -18;
+  {$EXTERNALSYM PCRE_ERROR_DFA_UMLIMIT}
+  PCRE_ERROR_DFA_WSSIZE = -19;
+  {$EXTERNALSYM PCRE_ERROR_DFA_WSSIZE}
+  PCRE_ERROR_DFA_RECURSE = -20;
+  {$EXTERNALSYM PCRE_ERROR_DFA_RECURSE}
+  PCRE_ERROR_RECURSIONLIMIT = -21;
+  {$EXTERNALSYM PCRE_ERROR_RECURSIONLIMIT}
+  PCRE_ERROR_NULLWSLIMIT = -22;  (* No longer actually used *)
+  {$EXTERNALSYM PCRE_ERROR_NULLWSLIMIT}
+  PCRE_ERROR_BADNEWLINE = -23;
+  {$EXTERNALSYM PCRE_ERROR_BADNEWLINE}
+
+  (* Request types for pcre_fullinfo() *)
+
+  PCRE_INFO_OPTIONS = 0;
+  {$EXTERNALSYM PCRE_INFO_OPTIONS}
+  PCRE_INFO_SIZE = 1;
+  {$EXTERNALSYM PCRE_INFO_SIZE}
+  PCRE_INFO_CAPTURECOUNT = 2;
+  {$EXTERNALSYM PCRE_INFO_CAPTURECOUNT}
+  PCRE_INFO_BACKREFMAX = 3;
+  {$EXTERNALSYM PCRE_INFO_BACKREFMAX}
+  PCRE_INFO_FIRSTCHAR = 4;
+  {$EXTERNALSYM PCRE_INFO_FIRSTCHAR}
+  PCRE_INFO_FIRSTTABLE = 5;
+  {$EXTERNALSYM PCRE_INFO_FIRSTTABLE}
+  PCRE_INFO_LASTLITERAL = 6;
+  {$EXTERNALSYM PCRE_INFO_LASTLITERAL}
+  PCRE_INFO_NAMEENTRYSIZE = 7;
+  {$EXTERNALSYM PCRE_INFO_NAMEENTRYSIZE}
+  PCRE_INFO_NAMECOUNT = 8;
+  {$EXTERNALSYM PCRE_INFO_NAMECOUNT}
+  PCRE_INFO_NAMETABLE = 9;
+  {$EXTERNALSYM PCRE_INFO_NAMETABLE}
+  PCRE_INFO_STUDYSIZE = 10;
+  {$EXTERNALSYM PCRE_INFO_STUDYSIZE}
+  PCRE_INFO_DEFAULT_TABLES = 11;
+  {$EXTERNALSYM PCRE_INFO_DEFAULT_TABLES}
+  PCRE_INFO_OKPARTIAL = 12;
+  {$EXTERNALSYM PCRE_INFO_OKPARTIAL}
+  PCRE_INFO_JCHANGED = 13;
+  {$EXTERNALSYM PCRE_INFO_JCHANGED}
+  PCRE_INFO_HASCRORLF = 14;
+  {$EXTERNALSYM PCRE_INFO_HASCRORLF}
+
+  (* Request types for pcre_config() *)
+  PCRE_CONFIG_UTF8 = 0;
+  {$EXTERNALSYM PCRE_CONFIG_UTF8}
+  PCRE_CONFIG_NEWLINE = 1;
+  {$EXTERNALSYM PCRE_CONFIG_NEWLINE}
+  PCRE_CONFIG_LINK_SIZE = 2;
+  {$EXTERNALSYM PCRE_CONFIG_LINK_SIZE}
+  PCRE_CONFIG_POSIX_MALLOC_THRESHOLD = 3;
+  {$EXTERNALSYM PCRE_CONFIG_POSIX_MALLOC_THRESHOLD}
+  PCRE_CONFIG_MATCH_LIMIT = 4;
+  {$EXTERNALSYM PCRE_CONFIG_MATCH_LIMIT}
+  PCRE_CONFIG_STACKRECURSE = 5;
+  {$EXTERNALSYM PCRE_CONFIG_STACKRECURSE}
+  PCRE_CONFIG_UNICODE_PROPERTIES = 6;
+  {$EXTERNALSYM PCRE_CONFIG_UNICODE_PROPERTIES}
+  PCRE_CONFIG_MATCH_LIMIT_RECURSION = 7;
+  {$EXTERNALSYM PCRE_CONFIG_MATCH_LIMIT_RECURSION}
+  PCRE_CONFIG_BSR = 8;
+  {$EXTERNALSYM PCRE_CONFIG_BSR}
+
+  (* Bit flags for the pcre_extra structure *)
+
+  PCRE_EXTRA_STUDY_DATA = $0001;
+  {$EXTERNALSYM PCRE_EXTRA_STUDY_DATA}
+  PCRE_EXTRA_MATCH_LIMIT = $0002;
+  {$EXTERNALSYM PCRE_EXTRA_MATCH_LIMIT}
+  PCRE_EXTRA_CALLOUT_DATA = $0004;
+  {$EXTERNALSYM PCRE_EXTRA_CALLOUT_DATA}
+  PCRE_EXTRA_TABLES = $0008;
+  {$EXTERNALSYM PCRE_EXTRA_TABLES}
+  PCRE_EXTRA_MATCH_LIMIT_RECURSION = $0010;
+  {$EXTERNALSYM PCRE_EXTRA_MATCH_LIMIT_RECURSION}
+
+type
+  (* Types *)
+  PPAnsiChar = ^PAnsiChar;
+  {$EXTERNALSYM PPAnsiChar}
+  PPPAnsiChar = ^PPAnsiChar;
+  {$EXTERNALSYM PPPAnsiChar}
+  PInteger = ^Integer;
+  {$EXTERNALSYM PInteger}
+
+  real_pcre = packed record
+    {magic_number: Longword;
+    size: Integer;
+    tables: PAnsiChar;
+    options: Longword;
+    top_bracket: Word;
+    top_backref: word;
+    first_char: PAnsiChar;
+    req_char: PAnsiChar;
+    code: array [0..0] of AnsiChar;}
+  end;
+  TPCRE = real_pcre;
+  PPCRE = ^TPCRE;
+
+  real_pcre_extra = packed record
+    {options: PAnsiChar;
+    start_bits: array [0..31] of AnsiChar;}
+    flags: Cardinal;        (* Bits for which fields are set *)
+    study_data: Pointer;    (* Opaque data from pcre_study() *)
+    match_limit: Cardinal;  (* Maximum number of calls to match() *)
+    callout_data: Pointer;  (* Data passed back in callouts *)
+    tables: PAnsiChar;      (* Pointer to character tables *)
+    match_limit_recursion: Cardinal; (* Max recursive calls to match() *)
+  end;
+  TPCREExtra = real_pcre_extra;
+  PPCREExtra = ^TPCREExtra;
+
+  pcre_callout_block = packed record
+    version: Integer;           (* Identifies version of block *)
+  (* ------------------------ Version 0 ------------------------------- *)
+    callout_number: Integer;    (* Number compiled into pattern *)
+    offset_vector: PInteger;    (* The offset vector *)
+    subject: PAnsiChar;         (* The subject being matched *)
+    subject_length: Integer;    (* The length of the subject *)
+    start_match: Integer;       (* Offset to start of this match attempt *)
+    current_position: Integer;  (* Where we currently are in the subject *)
+    capture_top: Integer;       (* Max current capture *)
+    capture_last: Integer;      (* Most recently closed capture *)
+    callout_data: Pointer;      (* Data passed in with the call *)
+  (* ------------------- Added for Version 1 -------------------------- *)
+    pattern_position: Integer;  (* Offset to next item in the pattern *)
+    next_item_length: Integer;  (* Length of next item in the pattern *)
+  (* ------------------------------------------------------------------ *)
+  end;
+
+  pcre_malloc_callback = function(Size: Integer): Pointer; {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+  {$EXTERNALSYM pcre_malloc_callback}
+  pcre_free_callback = procedure(P: Pointer); {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+  {$EXTERNALSYM pcre_free_callback}
+  pcre_stack_malloc_callback = function(Size: Integer): Pointer; {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+  {$EXTERNALSYM pcre_stack_malloc_callback}
+  pcre_stack_free_callback = procedure(P: Pointer); {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+  {$EXTERNALSYM pcre_stack_free_callback}
+  pcre_callout_callback = function(var callout_block: pcre_callout_block): Integer; {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+  {$EXTERNALSYM pcre_callout_callback}
+
+var
+  // renamed from "pcre_X" to "pcre_X_func" to allow functions with name "pcre_X" to be
+  // declared in implementation when static linked
+  pcre_malloc_func: ^pcre_malloc_callback = nil;
+  {$EXTERNALSYM pcre_malloc_func}
+  pcre_free_func: ^pcre_free_callback = nil;
+  {$EXTERNALSYM pcre_free_func}
+  pcre_stack_malloc_func: ^pcre_stack_malloc_callback = nil;
+  {$EXTERNALSYM pcre_stack_malloc_func}
+  pcre_stack_free_func: ^pcre_stack_free_callback = nil;
+  {$EXTERNALSYM pcre_stack_free_func}
+  pcre_callout_func: ^pcre_callout_callback = nil;
+  {$EXTERNALSYM pcre_callout_func}
+
+procedure SetPCREMallocCallback(const Value: pcre_malloc_callback);
+{$EXTERNALSYM SetPCREMallocCallback}
+function GetPCREMallocCallback: pcre_malloc_callback;
+{$EXTERNALSYM GetPCREMallocCallback}
+function CallPCREMalloc(Size: Integer): Pointer;
+{$EXTERNALSYM CallPCREMalloc}
+
+procedure SetPCREFreeCallback(const Value: pcre_free_callback);
+{$EXTERNALSYM SetPCREFreeCallback}
+function GetPCREFreeCallback: pcre_free_callback;
+{$EXTERNALSYM GetPCREFreeCallback}
+procedure CallPCREFree(P: Pointer);
+{$EXTERNALSYM CallPCREFree}
+
+procedure SetPCREStackMallocCallback(const Value: pcre_stack_malloc_callback);
+{$EXTERNALSYM SetPCREStackMallocCallback}
+function GetPCREStackMallocCallback: pcre_stack_malloc_callback;
+{$EXTERNALSYM GetPCREStackMallocCallback}
+function CallPCREStackMalloc(Size: Integer): Pointer;
+{$EXTERNALSYM CallPCREStackMalloc}
+
+procedure SetPCREStackFreeCallback(const Value: pcre_stack_free_callback);
+{$EXTERNALSYM SetPCREStackFreeCallback}
+function GetPCREStackFreeCallback: pcre_stack_free_callback;
+{$EXTERNALSYM GetPCREStackFreeCallback}
+procedure CallPCREStackFree(P: Pointer);
+{$EXTERNALSYM CallPCREStackFree}
+
+procedure SetPCRECalloutCallback(const Value: pcre_callout_callback);
+{$EXTERNALSYM SetPCRECalloutCallback}
+function GetPCRECalloutCallback: pcre_callout_callback;
+{$EXTERNALSYM GetPCRECalloutCallback}
+function CallPCRECallout(var callout_block: pcre_callout_block): Integer;
+{$EXTERNALSYM CallPCRECallout}
+
+type
+  TPCRELibNotLoadedHandler = procedure; {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+
+var
+  // Value to initialize function pointers below with, in case LoadPCRE fails
+  // or UnloadPCRE is called.  Typically the handler will raise an exception.
+  LibNotLoadedHandler: TPCRELibNotLoadedHandler = nil;
+
+(* Functions *)
+
+{$IFNDEF PCRE_LINKONREQUEST}
+// static link and static dll import
+function pcre_compile(const pattern: PAnsiChar; options: Integer;
+  const errptr: PPAnsiChar; erroffset: PInteger; const tableptr: PAnsiChar): PPCRE;
+  {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+{$EXTERNALSYM pcre_compile}
+function pcre_compile2(const pattern: PAnsiChar; options: Integer;
+  const errorcodeptr: PInteger; const errorptr: PPAnsiChar; erroroffset: PInteger;
+  const tables: PAnsiChar): PPCRE;
+  {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+{$EXTERNALSYM pcre_compile2}
+function pcre_config(what: Integer; where: Pointer): Integer;
+  {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+{$EXTERNALSYM pcre_config}
+function pcre_copy_named_substring(const code: PPCRE; const subject: PAnsiChar;
+  ovector: PInteger; stringcount: Integer; const stringname: PAnsiChar;
+  buffer: PAnsiChar; size: Integer): Integer;
+  {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+{$EXTERNALSYM pcre_copy_named_substring}
+function pcre_copy_substring(const subject: PAnsiChar; ovector: PInteger;
+  stringcount, stringnumber: Integer; buffer: PAnsiChar; buffersize: Integer): Integer;
+  {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+{$EXTERNALSYM pcre_copy_substring}
+function pcre_dfa_exec(const argument_re: PPCRE; const extra_data: PPCREExtra;
+  const subject: PAnsiChar; length: Integer; start_offset: Integer;
+  options: Integer; offsets: PInteger; offsetcount: Integer; workspace: PInteger;
+  wscount: Integer): Integer;
+  {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+{$EXTERNALSYM pcre_dfa_exec}
+function pcre_exec(const code: PPCRE; const extra: PPCREExtra; const subject: PAnsiChar;
+  length, startoffset, options: Integer; ovector: PInteger; ovecsize: Integer): Integer;
+  {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+{$EXTERNALSYM pcre_exec}
+procedure pcre_free_substring(stringptr: PAnsiChar);
+  {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+{$EXTERNALSYM pcre_free_substring}
+procedure pcre_free_substring_list(stringlistptr: PPAnsiChar);
+  {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+{$EXTERNALSYM pcre_free_substring_list}
+function pcre_fullinfo(const code: PPCRE; const extra: PPCREExtra;
+  what: Integer; where: Pointer): Integer;
+  {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+{$EXTERNALSYM pcre_fullinfo}
+function pcre_get_named_substring(const code: PPCRE; const subject: PAnsiChar;
+  ovector: PInteger; stringcount: Integer; const stringname: PAnsiChar;
+  const stringptr: PPAnsiChar): Integer;
+  {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+{$EXTERNALSYM pcre_get_named_substring}
+function pcre_get_stringnumber(const code: PPCRE; const stringname: PAnsiChar): Integer;
+  {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+{$EXTERNALSYM pcre_get_stringnumber}
+function pcre_get_stringtable_entries(const code: PPCRE; const stringname: PAnsiChar;
+  firstptr: PPAnsiChar; lastptr: PPAnsiChar): Integer;
+  {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+{$EXTERNALSYM pcre_get_stringtable_entries}
+function pcre_get_substring(const subject: PAnsiChar; ovector: PInteger;
+  stringcount, stringnumber: Integer; const stringptr: PPAnsiChar): Integer;
+  {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+{$EXTERNALSYM pcre_get_substring}
+function pcre_get_substring_list(const subject: PAnsiChar; ovector: PInteger;
+  stringcount: Integer; listptr: PPPAnsiChar): Integer;
+  {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+{$EXTERNALSYM pcre_get_substring_list}
+function pcre_info(const code: PPCRE; optptr, firstcharptr: PInteger): Integer;
+  {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+{$EXTERNALSYM pcre_info}
+function pcre_maketables: PAnsiChar;
+  {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+{$EXTERNALSYM pcre_maketables}
+function pcre_refcount(argument_re: PPCRE; adjust: Integer): Integer;
+  {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+{$EXTERNALSYM pcre_refcount}
+function pcre_study(const code: PPCRE; options: Integer; const errptr: PPAnsiChar): PPCREExtra;
+  {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+{$EXTERNALSYM pcre_study}
+function pcre_version: PAnsiChar; {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+{$EXTERNALSYM pcre_version}
+
+// Calling pcre_free in the DLL causes an access violation error; use pcre_dispose instead
+procedure pcre_dispose(pattern, hints, chartable: Pointer); {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+
+{$ELSE}
+// dynamic dll import
+type
+  pcre_compile_func = function(const pattern: PAnsiChar; options: Integer;
+    const errptr: PPAnsiChar; erroffset: PInteger; const tableptr: PAnsiChar): PPCRE;
+    {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+  {$EXTERNALSYM pcre_compile_func}
+  pcre_compile2_func = function(const pattern: PAnsiChar; options: Integer;
+    const errorcodeptr: PInteger; const errorptr: PPAnsiChar; erroroffset: PInteger;
+    const tables: PAnsiChar): PPCRE; {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+  {$EXTERNALSYM pcre_compile2_func}
+  pcre_config_func = function(what: Integer; where: Pointer): Integer;
+  {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+  {$EXTERNALSYM pcre_config_func}
+  pcre_copy_named_substring_func = function(const code: PPCRE; const subject: PAnsiChar;
+    ovector: PInteger; stringcount: Integer; const stringname: PAnsiChar;
+    buffer: PAnsiChar; size: Integer): Integer; {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+  {$EXTERNALSYM pcre_copy_named_substring_func}
+  pcre_copy_substring_func = function(const subject: PAnsiChar; ovector: PInteger;
+    stringcount, stringnumber: Integer; buffer: PAnsiChar; buffersize: Integer): Integer;
+    {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+  {$EXTERNALSYM pcre_copy_substring_func}
+  pcre_dfa_exec_func = function(const argument_re: PPCRE; const extra_data: PPCREExtra;
+    const subject: PAnsiChar; length: Integer; start_offset: Integer;
+    options: Integer; offsets: PInteger; offsetcount: Integer; workspace: PInteger;
+    wscount: Integer): Integer; {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+  {$EXTERNALSYM pcre_dfa_exec_func}
+  pcre_exec_func = function(const code: PPCRE; const extra: PPCREExtra; const subject: PAnsiChar;
+    length, startoffset, options: Integer; ovector: PInteger; ovecsize: Integer): Integer;
+    {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+  {$EXTERNALSYM pcre_exec_func}
+  pcre_free_substring_func = procedure(stringptr: PAnsiChar);
+    {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+  {$EXTERNALSYM pcre_free_substring_func}
+  pcre_free_substring_list_func = procedure(stringptr: PPAnsiChar);
+    {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+  {$EXTERNALSYM pcre_free_substring_list_func}
+  pcre_fullinfo_func = function(const code: PPCRE; const extra: PPCREExtra;
+    what: Integer; where: Pointer): Integer;
+    {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+  {$EXTERNALSYM pcre_fullinfo_func}
+  pcre_get_named_substring_func = function(const code: PPCRE; const subject: PAnsiChar;
+    ovector: PInteger; stringcount: Integer; const stringname: PAnsiChar;
+    const stringptr: PPAnsiChar): Integer; {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+  {$EXTERNALSYM pcre_get_named_substring_func}
+  pcre_get_stringnumber_func = function(const code: PPCRE;
+    const stringname: PAnsiChar): Integer; {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+  {$EXTERNALSYM pcre_get_stringnumber_func}
+  pcre_get_stringtable_entries_func = function(const code: PPCRE; const stringname: PAnsiChar;
+    firstptr: PPAnsiChar; lastptr: PPAnsiChar): Integer;
+    {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+  {$EXTERNALSYM pcre_get_stringtable_entries_func}
+  pcre_get_substring_func = function(const subject: PAnsiChar; ovector: PInteger;
+    stringcount, stringnumber: Integer; const stringptr: PPAnsiChar): Integer;
+    {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+  {$EXTERNALSYM pcre_get_substring_func}
+  pcre_get_substring_list_func = function(const subject: PAnsiChar; ovector: PInteger;
+    stringcount: Integer; listptr: PPPAnsiChar): Integer;
+    {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+  {$EXTERNALSYM pcre_get_substring_list_func}
+  pcre_info_func = function(const code: PPCRE; optptr, firstcharptr: PInteger): Integer;
+  {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+  {$EXTERNALSYM pcre_info_func}
+  pcre_maketables_func = function: PAnsiChar; {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+  {$EXTERNALSYM pcre_maketables_func}
+  pcre_refcount_func = function(argument_re: PPCRE; adjust: Integer): Integer;
+  {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+  {$EXTERNALSYM pcre_refcount_func}
+  pcre_study_func = function(const code: PPCRE; options: Integer; const errptr: PPAnsiChar): PPCREExtra;
+  {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+  {$EXTERNALSYM pcre_study_func}
+  pcre_version_func = function: PAnsiChar; {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL}
+  {$EXTERNALSYM pcre_version_func}
+
+var
+  pcre_compile: pcre_compile_func = nil;
+  {$EXTERNALSYM pcre_compile}
+  pcre_compile2: pcre_compile2_func = nil;
+  {$EXTERNALSYM pcre_compile2}
+  pcre_config: pcre_config_func = nil;
+  {$EXTERNALSYM pcre_config}
+  pcre_copy_named_substring: pcre_copy_named_substring_func = nil;
+  {$EXTERNALSYM pcre_copy_named_substring}
+  pcre_copy_substring: pcre_copy_substring_func = nil;
+  {$EXTERNALSYM pcre_copy_substring}
+  pcre_dfa_exec: pcre_dfa_exec_func = nil;
+  {$EXTERNALSYM pcre_dfa_exec}
+  pcre_exec: pcre_exec_func = nil;
+  {$EXTERNALSYM pcre_exec}
+  pcre_free_substring: pcre_free_substring_func = nil;
+  {$EXTERNALSYM pcre_free_substring}
+  pcre_free_substring_list: pcre_free_substring_list_func = nil;
+  {$EXTERNALSYM pcre_free_substring_list}
+  pcre_fullinfo: pcre_fullinfo_func = nil;
+  {$EXTERNALSYM pcre_fullinfo}
+  pcre_get_named_substring: pcre_get_named_substring_func = nil;
+  {$EXTERNALSYM pcre_get_named_substring}
+  pcre_get_stringnumber: pcre_get_stringnumber_func = nil;
+  {$EXTERNALSYM pcre_get_stringnumber}
+  pcre_get_stringtable_entries: pcre_get_stringtable_entries_func = nil;
+  {$EXTERNALSYM pcre_get_stringtable_entries}
+  pcre_get_substring: pcre_get_substring_func = nil;
+  {$EXTERNALSYM pcre_get_substring}
+  pcre_get_substring_list: pcre_get_substring_list_func = nil;
+  {$EXTERNALSYM pcre_get_substring_list}
+  pcre_info: pcre_info_func = nil;
+  {$EXTERNALSYM pcre_info}
+  pcre_maketables: pcre_maketables_func = nil;
+  {$EXTERNALSYM pcre_maketables}
+  pcre_refcount: pcre_refcount_func = nil;
+  {$EXTERNALSYM pcre_refcount}
+  pcre_study: pcre_study_func = nil;
+  {$EXTERNALSYM pcre_study}
+  pcre_version: pcre_version_func = nil;
+  {$EXTERNALSYM pcre_version}
+
+{$ENDIF ~PCRE_LINKONREQUEST}
+
+function IsPCRELoaded: Boolean;
+function LoadPCRE: Boolean;
+procedure UnloadPCRE;
+
+implementation
+
+uses
+  SysUtils,
+  {$IFDEF MSWINDOWS}
+  Windows;
+  {$ENDIF MSWINDOWS}
+  {$IFDEF UNIX}
+  {$IFDEF HAS_UNIT_TYPES}
+  Types,
+  {$ENDIF HAS_UNIT_TYPES}
+  {$IFDEF HAS_UNIT_LIBC}
+  Libc;
+  {$ELSE ~HAS_UNIT_LIBC}
+  dl;
+  {$ENDIF ~HAS_UNIT_LIBC}
+  {$ENDIF UNIX}
+
+{$IFDEF PCRE_STATICLINK}
+{$LINK pcre\pcre_compile.obj}
+{$LINK pcre\pcre_config.obj}
+{$LINK pcre\pcre_dfa_exec.obj}
+{$LINK pcre\pcre_exec.obj}
+{$LINK pcre\pcre_fullinfo.obj}
+{$LINK pcre\pcre_get.obj}
+{$LINK pcre\pcre_globals.obj}
+{$LINK pcre\pcre_info.obj}
+{$LINK pcre\pcre_maketables.obj}
+{$LINK pcre\pcre_newline.obj}
+{$LINK pcre\pcre_ord2utf8.obj}
+{$LINK pcre\pcre_refcount.obj}
+{$LINK pcre\pcre_study.obj}
+{$LINK pcre\pcre_tables.obj}
+{$LINK pcre\pcre_try_flipped.obj}
+{$LINK pcre\pcre_ucd.obj}
+{$LINK pcre\pcre_valid_utf8.obj}
+{$LINK pcre\pcre_version.obj}
+{$LINK pcre\pcre_xclass.obj}
+{$LINK pcre\pcre_default_tables.obj}
+
+// user's defined callbacks
+var
+  pcre_malloc_user: pcre_malloc_callback;
+  pcre_free_user: pcre_free_callback;
+  pcre_stack_malloc_user: pcre_stack_malloc_callback;
+  pcre_stack_free_user: pcre_stack_free_callback;
+  pcre_callout_user: pcre_callout_callback;
+
+function pcre_compile; external;
+function pcre_compile2; external;
+function pcre_config; external;
+function pcre_copy_named_substring; external;
+function pcre_copy_substring; external;
+function pcre_dfa_exec; external;
+function pcre_exec; external;
+procedure pcre_free_substring; external;
+procedure pcre_free_substring_list; external;
+function pcre_fullinfo; external;
+function pcre_get_named_substring; external;
+function pcre_get_stringnumber; external;
+function pcre_get_stringtable_entries; external;
+function pcre_get_substring; external;
+function pcre_get_substring_list; external;
+function pcre_info; external;
+function pcre_maketables; external;
+function pcre_refcount; external;
+function pcre_study; external;
+function pcre_version; external;
+
+type
+  size_t = Longint;
+
+const
+  szMSVCRT = 'MSVCRT.DLL';
+
+function _memcpy(dest, src: Pointer; count: size_t): Pointer; cdecl; external szMSVCRT name 'memcpy';
+function _memmove(dest, src: Pointer; count: size_t): Pointer; cdecl; external szMSVCRT name 'memmove';
+function _memset(dest: Pointer; val: Integer; count: size_t): Pointer; cdecl; external szMSVCRT name 'memset';
+function _strncmp(s1: PAnsiChar; s2: PAnsiChar; n: size_t): Integer; cdecl; external szMSVCRT name 'strncmp';
+function _memcmp(s1: Pointer; s2: Pointer; n: size_t): Integer; cdecl; external szMSVCRT name 'memcmp';
+function _strlen(s: PAnsiChar): size_t; cdecl; external szMSVCRT name 'strlen';
+function __ltolower(__ch: Integer): Integer; cdecl; external szMSVCRT name 'tolower';
+function __ltoupper(__ch: Integer): Integer; cdecl; external szMSVCRT name 'toupper';
+function _isalnum(__ch: Integer): Integer; cdecl; external szMSVCRT name 'isalnum';
+function _isalpha(__ch: Integer): Integer; cdecl; external szMSVCRT name 'isalpha';
+function _iscntrl(__ch: Integer): Integer; cdecl; external szMSVCRT name 'iscntrl';
+function _isdigit(__ch: Integer): Integer; cdecl; external szMSVCRT name 'isdigit';
+function _isgraph(__ch: Integer): Integer; cdecl; external szMSVCRT name 'isgraph';
+function _islower(__ch: Integer): Integer; cdecl; external szMSVCRT name 'islower';
+function _isprint(__ch: Integer): Integer; cdecl; external szMSVCRT name 'isprint';
+function _ispunct(__ch: Integer): Integer; cdecl; external szMSVCRT name 'ispunct';
+function _isspace(__ch: Integer): Integer; cdecl; external szMSVCRT name 'isspace';
+function _isupper(__ch: Integer): Integer; cdecl; external szMSVCRT name 'isupper';
+function _isxdigit(__ch: Integer): Integer; cdecl; external szMSVCRT name 'isxdigit';
+function _strchr(__s: PAnsiChar; __c: Integer): PAnsiChar; cdecl; external szMSVCRT name 'strchr';
+
+function malloc(size: size_t): Pointer; cdecl; external szMSVCRT name 'malloc';
+
+function pcre_malloc(Size: Integer): Pointer;
+begin
+  if Assigned(pcre_malloc_user) then
+    Result := pcre_malloc_user(Size)
+  else
+    Result := malloc(Size);
+end;
+
+function pcre_stack_malloc(Size: Integer): Pointer;
+begin
+  if Assigned(pcre_stack_malloc_user) then
+    Result := pcre_stack_malloc_user(Size)
+  else
+    Result := malloc(Size);
+end;
+
+function _malloc(size: size_t): Pointer;
+begin
+  Result := pcre_malloc(size);
+end;
+
+procedure free(pBlock: Pointer); cdecl; external szMSVCRT name 'free';
+
+procedure pcre_free(P: Pointer);
+begin
+  if Assigned(pcre_free_user) then
+    pcre_free_user(P)
+  else
+    free(P);
+end;
+
+procedure pcre_stack_free(P: Pointer);
+begin
+  if Assigned(pcre_stack_free_user) then
+    pcre_stack_free_user(P)
+  else
+    free(P);
+end;
+
+procedure _free(pBlock: Pointer);
+begin
+  pcre_free(pBlock);
+end;
+
+function pcre_callout(var callout_block: pcre_callout_block): Integer; cdecl;
+begin
+  if Assigned(pcre_callout_user) then
+    Result := pcre_callout_user(callout_block)
+  else
+    Result := 0;
+end;
+
+{$ELSE ~PCRE_STATICLINK}
+
+type
+  {$IFDEF MSWINDOWS}
+  TModuleHandle = HINST;
+  {$ENDIF MSWINDOWS}
+  {$IFDEF LINUX}
+  TModuleHandle = Pointer;
+  {$ENDIF LINUX}
+
+const
+  {$IFDEF MSWINDOWS}
+  libpcremodulename = 'pcrelib.dll';
+  {$ENDIF MSWINDOWS}
+  {$IFDEF UNIX}
+  libpcremodulename = 'libpcre.so.0';
+  {$ENDIF UNIX}
+  PCRECompileExportName = 'pcre_compile';
+  PCRECompile2ExportName = 'pcre_compile2';
+  PCREConfigExportName = 'pcre_config';
+  PCRECopyNamedSubstringExportName = 'pcre_copy_named_substring';
+  PCRECopySubStringExportName = 'pcre_copy_substring';
+  PCREDfaExecExportName = 'pcre_dfa_exec';
+  PCREExecExportName = 'pcre_exec';
+  PCREFreeSubStringExportName = 'pcre_free_substring';
+  PCREFreeSubStringListExportName = 'pcre_free_substring_list';
+  PCREFullInfoExportName = 'pcre_fullinfo';
+  PCREGetNamedSubstringExportName = 'pcre_get_named_substring';
+  PCREGetStringNumberExportName = 'pcre_get_stringnumber';
+  PCREGetStringTableEntriesExportName = 'pcre_get_stringtable_entries';
+  PCREGetSubStringExportName = 'pcre_get_substring';
+  PCREGetSubStringListExportName = 'pcre_get_substring_list';
+  PCREInfoExportName = 'pcre_info';
+  PCREMakeTablesExportName = 'pcre_maketables';
+  PCRERefCountExportName = 'pcre_refcount';
+  PCREStudyExportName = 'pcre_study';
+  PCREVersionExportName = 'pcre_version';
+  PCREMallocExportName = 'pcre_malloc';
+  PCREFreeExportName = 'pcre_free';
+  PCREStackMallocExportName = 'pcre_stack_malloc';
+  PCREStackFreeExportName = 'pcre_stack_free';
+  PCRECalloutExportName = 'pcre_callout';
+  INVALID_MODULEHANDLE_VALUE = TModuleHandle(0);
+
+var
+  PCRELib: TModuleHandle = INVALID_MODULEHANDLE_VALUE;
+{$ENDIF ~PCRE_STATICLINK}
+
+procedure SetPCREMallocCallback(const Value: pcre_malloc_callback);
+begin
+  {$IFDEF PCRE_STATICLINK}
+  pcre_malloc_user := Value;
+  {$ELSE ~PCRE_STATICLINK}
+  if not Assigned(pcre_malloc_func) then
+    LoadPCRE;
+
+  if Assigned(pcre_malloc_func) then
+    pcre_malloc_func^ := Value
+  else if Assigned(LibNotLoadedHandler) then
+    LibNotLoadedHandler;
+  {$ENDIF ~PCRE_STATICLINK}
+end;
+
+function GetPCREMallocCallback: pcre_malloc_callback;
+begin
+  {$IFDEF PCRE_STATICLINK}
+  Result := pcre_malloc_user;
+  {$ELSE ~PCRE_STATICLINK}
+  if not Assigned(pcre_malloc_func) then
+    LoadPCRE;
+
+  if not Assigned(pcre_malloc_func) then
+  begin
+    Result := nil;
+    if Assigned(LibNotLoadedHandler) then
+      LibNotLoadedHandler;
+  end
+  else
+    Result := pcre_malloc_func^;
+  {$ENDIF ~PCRE_STATICLINK}
+end;
+
+function CallPCREMalloc(Size: Integer): Pointer;
+begin
+  {$IFDEF PCRE_STATICLINK}
+  Result := pcre_malloc(Size);
+  {$ELSE ~PCRE_STATICLINK}
+  Result := pcre_malloc_func^(Size);
+  {$ENDIF ~PCRE_STATICLINK}
+end;
+
+procedure SetPCREFreeCallback(const Value: pcre_free_callback);
+begin
+  {$IFDEF PCRE_STATICLINK}
+  pcre_free_user := Value;
+  {$ELSE ~PCRE_STATICLINK}
+  if not Assigned(pcre_free_func) then
+    LoadPCRE;
+
+  if Assigned(pcre_free_func) then
+    pcre_free_func^ := Value
+  else if Assigned(LibNotLoadedHandler) then
+    LibNotLoadedHandler;
+  {$ENDIF ~PCRE_STATICLINK}
+end;
+
+function GetPCREFreeCallback: pcre_free_callback;
+begin
+  {$IFDEF PCRE_STATICLINK}
+  Result := pcre_free_user;
+  {$ELSE ~PCRE_STATICLINK}
+  if not Assigned(pcre_free_func) then
+    LoadPCRE;
+
+  if not Assigned(pcre_free_func) then
+  begin
+    Result := nil;
+    if Assigned(LibNotLoadedHandler) then
+      LibNotLoadedHandler;
+  end
+  else
+    Result := pcre_free_func^
+  {$ENDIF ~PCRE_STATICLINK}
+end;
+
+procedure CallPCREFree(P: Pointer);
+begin
+  {$IFDEF PCRE_STATICLINK}
+  pcre_free(P);
+  {$ELSE ~PCRE_STATICLINK}
+  if not Assigned(pcre_free_func) then
+    LoadPCRE;
+  pcre_free_func^(P);
+  {$ENDIF ~PCRE_STATICLINK}
+end;
+
+procedure SetPCREStackMallocCallback(const Value: pcre_stack_malloc_callback);
+begin
+  {$IFDEF PCRE_STATICLINK}
+  pcre_stack_malloc_user := Value;
+  {$ELSE ~PCRE_STATICLINK}
+  if not Assigned(pcre_stack_malloc_func) then
+    LoadPCRE;
+
+  if Assigned(pcre_stack_malloc_func) then
+    pcre_stack_malloc_func^ := Value
+  else if Assigned(LibNotLoadedHandler) then
+    LibNotLoadedHandler;
+  {$ENDIF ~PCRE_STATICLINK}
+end;
+
+function GetPCREStackMallocCallback: pcre_stack_malloc_callback;
+begin
+  {$IFDEF PCRE_STATICLINK}
+  Result := pcre_stack_malloc_user;
+  {$ELSE ~PCRE_STATICLINK}
+  if not Assigned(pcre_stack_malloc_func) then
+    LoadPCRE;
+
+  if not Assigned(pcre_stack_malloc_func) then
+  begin
+    Result := nil;
+    if Assigned(LibNotLoadedHandler) then
+      LibNotLoadedHandler;
+  end
+  else
+    Result := pcre_stack_malloc_func^;
+  {$ENDIF ~PCRE_STATICLINK}
+end;
+
+function CallPCREStackMalloc(Size: Integer): Pointer;
+begin
+  {$IFDEF PCRE_STATICLINK}
+  Result := pcre_stack_malloc(Size);
+  {$ELSE ~PCRE_STATICLINK}
+  Result := pcre_stack_malloc_func^(Size);
+  {$ENDIF ~PCRE_STATICLINK}
+end;
+
+procedure SetPCREStackFreeCallback(const Value: pcre_stack_free_callback);
+begin
+  {$IFDEF PCRE_STATICLINK}
+  pcre_stack_free_user := Value;
+  {$ELSE ~PCRE_STATICLINK}
+  if not Assigned(pcre_stack_free_func) then
+    LoadPCRE;
+
+  if Assigned(pcre_stack_free_func) then
+    pcre_stack_free_func^ := Value
+  else if Assigned(LibNotLoadedHandler) then
+    LibNotLoadedHandler;
+  {$ENDIF ~PCRE_STATICLINK}
+end;
+
+function GetPCREStackFreeCallback: pcre_stack_free_callback;
+begin
+  {$IFDEF PCRE_STATICLINK}
+  Result := pcre_stack_free_user;
+  {$ELSE ~PCRE_STATICLINK}
+  if not Assigned(pcre_stack_free_func) then
+    LoadPCRE;
+
+  if not Assigned(pcre_stack_free_func) then
+  begin
+    Result := nil;
+    if Assigned(LibNotLoadedHandler) then
+      LibNotLoadedHandler;
+  end
+  else
+    Result := pcre_stack_free_func^;
+  {$ENDIF ~PCRE_STATICLINK}
+end;
+
+procedure CallPCREStackFree(P: Pointer);
+begin
+  {$IFDEF PCRE_STATICLINK}
+  pcre_stack_free(P);
+  {$ELSE ~PCRE_STATICLINK}
+  pcre_stack_free_func^(P);
+  {$ENDIF ~PCRE_STATICLINK}
+end;
+
+procedure SetPCRECalloutCallback(const Value: pcre_callout_callback);
+begin
+  {$IFDEF PCRE_STATICLINK}
+  pcre_callout_user := Value;
+  {$ELSE ~PCRE_STATICLINK}
+  if not Assigned(pcre_callout_func) then
+    LoadPCRE;
+
+  if Assigned(pcre_callout_func) then
+    pcre_callout_func^ := Value
+  else if Assigned(LibNotLoadedHandler) then
+    LibNotLoadedHandler;
+  {$ENDIF ~PCRE_STATICLINK}
+end;
+
+function GetPCRECalloutCallback: pcre_callout_callback;
+begin
+  {$IFDEF PCRE_STATICLINK}
+  Result := pcre_callout_user;
+  {$ELSE ~PCRE_STATICLINK}
+  if not Assigned(pcre_callout_func) then
+    LoadPCRE;
+
+  if not Assigned(pcre_callout_func) then
+  begin
+    Result := nil;
+    if Assigned(LibNotLoadedHandler) then
+      LibNotLoadedHandler;
+  end
+  else
+    Result := pcre_callout_func^;
+  {$ENDIF ~PCRE_STATICLINK}
+end;
+
+function CallPCRECallout(var callout_block: pcre_callout_block): Integer;
+begin
+  {$IFDEF PCRE_STATICLINK}
+  Result := pcre_callout(callout_block);
+  {$ELSE ~PCRE_STATICLINK}
+  Result := pcre_callout_func^(callout_block);
+  {$ENDIF ~PCRE_STATICLINK}
+end;
+
+{$IFNDEF PCRE_STATICLINK}
+procedure InitPCREFuncPtrs(const Value: Pointer);
+begin
+  {$IFDEF PCRE_LINKONREQUEST}
+  @pcre_compile := Value;
+  @pcre_compile2 := Value;
+  @pcre_config := Value;
+  @pcre_copy_named_substring := Value;
+  @pcre_copy_substring := Value;
+  @pcre_dfa_exec := Value;
+  @pcre_exec := Value;
+  @pcre_free_substring := Value;
+  @pcre_free_substring_list := Value;
+  @pcre_fullinfo := Value;
+  @pcre_get_named_substring := Value;
+  @pcre_get_stringnumber := Value;
+  @pcre_get_stringtable_entries := Value;
+  @pcre_get_substring := Value;
+  @pcre_get_substring_list := Value;
+  @pcre_info := Value;
+  @pcre_maketables := Value;
+  @pcre_refcount := Value;
+  @pcre_study := Value;
+  @pcre_version := Value;
+  {$ENDIF PCRE_LINKONREQUEST}
+  pcre_malloc_func := nil;
+  pcre_free_func := nil;
+  pcre_stack_malloc_func := nil;
+  pcre_stack_free_func := nil;
+  pcre_callout_func := nil;
+end;
+{$ENDIF ~PCRE_STATICLINK}
+
+function IsPCRELoaded: Boolean;
+begin
+  {$IFDEF PCRE_STATICLINK}
+  Result := True;
+  {$ELSE ~PCRE_STATICLINK}
+  Result := PCRELib <> INVALID_MODULEHANDLE_VALUE;
+  {$ENDIF ~PCRE_STATICLINK}
+end;
+
+function LoadPCRE: Boolean;
+{$IFDEF PCRE_STATICLINK}
+begin
+  Result := True;
+end;
+{$ELSE ~PCRE_STATICLINK}
+  function GetSymbol(SymbolName: PAnsiChar): Pointer;
+  begin
+    {$IFDEF MSWINDOWS}
+    Result := GetProcAddress(PCRELib, PChar(SymbolName));
+    {$ENDIF MSWINDOWS}
+    {$IFDEF UNIX}
+    Result := dlsym(PCRELib, PChar(SymbolName));
+    {$ENDIF UNIX}
+  end;
+
+begin
+  Result := PCRELib <> INVALID_MODULEHANDLE_VALUE;
+  if Result then
+    Exit;
+
+  if PCRELib = INVALID_MODULEHANDLE_VALUE then
+    {$IFDEF MSWINDOWS}
+    PCRELib := SafeLoadLibrary(libpcremodulename);
+    {$ENDIF MSWINDOWS}
+    {$IFDEF UNIX}
+    PCRELib := dlopen(PAnsiChar(libpcremodulename), RTLD_NOW);
+    {$ENDIF UNIX}
+  Result := PCRELib <> INVALID_MODULEHANDLE_VALUE;
+  if Result then
+  begin
+    {$IFDEF PCRE_LINKONREQUEST}
+    @pcre_compile := GetSymbol(PCRECompileExportName);
+    @pcre_compile2 := GetSymbol(PCRECompile2ExportName);
+    @pcre_config := GetSymbol(PCREConfigExportName);
+    @pcre_copy_named_substring := GetSymbol(PCRECopyNamedSubstringExportName);
+    @pcre_copy_substring := GetSymbol(PCRECopySubStringExportName);
+    @pcre_dfa_exec := GetSymbol(PCREDfaExecExportName);
+    @pcre_exec := GetSymbol(PCREExecExportName);
+    @pcre_free_substring := GetSymbol(PCREFreeSubStringExportName);
+    @pcre_free_substring_list := GetSymbol(PCREFreeSubStringListExportName);
+    @pcre_fullinfo := GetSymbol(PCREFullInfoExportName);
+    @pcre_get_named_substring := GetSymbol(PCREGetNamedSubstringExportName);
+    @pcre_get_stringnumber := GetSymbol(PCREGetStringNumberExportName);
+    @pcre_get_stringtable_entries := GetSymbol(PCREGetStringTableEntriesExportName);
+    @pcre_get_substring := GetSymbol(PCREGetSubStringExportName);
+    @pcre_get_substring_list := GetSymbol(PCREGetSubStringListExportName);
+    @pcre_info := GetSymbol(PCREInfoExportName);
+    @pcre_maketables := GetSymbol(PCREMakeTablesExportName);
+    @pcre_refcount := GetSymbol(PCRERefCountExportName);
+    @pcre_study := GetSymbol(PCREStudyExportName);
+    @pcre_version := GetSymbol(PCREVersionExportName);
+    {$ENDIF PCRE_LINKONREQUEST}
+    pcre_malloc_func := GetSymbol(PCREMallocExportName);
+    pcre_free_func := GetSymbol(PCREFreeExportName);
+    pcre_stack_malloc_func := GetSymbol(PCREStackMallocExportName);
+    pcre_stack_free_func := GetSymbol(PCREStackFreeExportName);
+    pcre_callout_func := GetSymbol(PCRECalloutExportName);
+  end
+  else
+    InitPCREFuncPtrs(@LibNotLoadedHandler);
+end;
+{$ENDIF ~PCRE_STATICLINK}
+
+procedure UnloadPCRE;
+begin
+  {$IFNDEF PCRE_STATICLINK}
+  if PCRELib <> INVALID_MODULEHANDLE_VALUE then
+    {$IFDEF MSWINDOWS}
+    FreeLibrary(PCRELib);
+    {$ENDIF MSWINDOWS}
+    {$IFDEF UNIX}
+    dlclose(Pointer(PCRELib));
+    {$ENDIF UNIX}
+  PCRELib := INVALID_MODULEHANDLE_VALUE;
+  InitPCREFuncPtrs(@LibNotLoadedHandler);
+  {$ENDIF ~PCRE_STATICLINK}
+end;
+
+{$IFDEF PCRE_STATICLINK}
+procedure pcre_dispose(pattern, hints, chartable: Pointer);
+begin
+  if pattern <> nil then pcre_free(pattern);
+  if hints <> nil then pcre_free(hints);
+  if chartable <> nil then pcre_free(chartable);
+end;
+{$ENDIF PCRE_STATICLINK}
+
+{$IFDEF PCRE_LINKDLL}
+function pcre_compile; external libpcremodulename name PCRECompileExportName;
+function pcre_compile2; external libpcremodulename name PCRECompile2ExportName;
+function pcre_config; external libpcremodulename name PCREConfigExportName;
+function pcre_copy_named_substring; external libpcremodulename name PCRECopyNamedSubStringExportName;
+function pcre_copy_substring; external libpcremodulename name PCRECopySubStringExportName;
+function pcre_dfa_exec; external libpcremodulename name PCREDfaExecExportName;
+function pcre_exec; external libpcremodulename name PCREExecExportName;
+procedure pcre_free_substring; external libpcremodulename name PCREFreeSubStringExportName;
+procedure pcre_free_substring_list; external libpcremodulename name PCREFreeSubStringListExportName;
+function pcre_fullinfo; external libpcremodulename name PCREFullInfoExportName;
+function pcre_get_named_substring; external libpcremodulename name PCREGetNamedSubStringExportName;
+function pcre_get_stringnumber; external libpcremodulename name PCREGetStringNumberExportName;
+function pcre_get_stringtable_entries; external libpcremodulename name PCREGetStringTableEntriesExportName;
+function pcre_get_substring; external libpcremodulename name PCREGetSubStringExportName;
+function pcre_get_substring_list; external libpcremodulename name PCREGetSubStringListExportName;
+function pcre_info; external libpcremodulename name PCREInfoExportName;
+function pcre_maketables; external libpcremodulename name PCREMakeTablesExportName;
+function pcre_refcount; external libpcremodulename name PCRERefCountExportName;
+function pcre_study; external libpcremodulename name PCREStudyExportName;
+function pcre_version; external libpcremodulename name PCREVersionExportName;
+procedure pcre_dispose; external libpcremodulename name 'pcre_dispose';
+{$ENDIF PCRE_LINKDLL}
+
+end.
+

+ 130 - 0
pcre/makefile.mak

@@ -0,0 +1,130 @@
+#
+# makefile to make pcre .obj files using Borland's C++ compiler bcc32
+# derived from a makefile generated by BCB6' bpr2mak
+#
+# if pcre source directory is different from $(JCL)\source\pcre-7.7, use
+# "make -Dpcresrc=<path to pcre sources>" to tell make where to find the 
+# source files
+#
+# Make.exe needs to reside in the same directory as bcc32.exe.
+# For example, if you have Borlands free C++ v. 5.5 compiler (available from
+# http://www.borland.com/products/downloads/download_cbuilder.html#) installed:
+#
+# >C:\Program Files\Borland\BCC55\Bin\make
+#
+# or, if you want to use C++ Builder 6:
+#
+# >C:\Program Files\Borland\CBuilder6\Bin\make
+#
+# or, if you want to use Borland Developer Studio 2006:
+#
+# >C:\Program files\Borland\BDS\4.0\bin\make
+#
+# To choose the target CPU, pass "-DCPU=n" as option to make, with n being a
+# number between 3 and 6, with the following meanings:
+#
+#   n   Target CPU (or compatible)
+# --------------------------------
+#   3   80386
+#   4   80486
+#   5   Pentium (default)
+#   6   Pentium Pro
+#
+# Robert Rossmair, 2004-10-16
+#
+
+CallingConvention = -pr
+
+!if !$d(BCB)
+BCB = $(MAKEDIR)\..
+!endif
+
+BCC = $(BCB)
+
+!if !$d(pcresrc)
+pcresrc = ..\..\..\pcre-7.7
+!endif
+
+!if !$d(CPU)
+CPU = 5         # Pentium
+!endif
+
+# ---------------------------------------------------------------------------
+# IDE SECTION
+# ---------------------------------------------------------------------------
+# The following section of the project makefile is managed by the BCB IDE.
+# It is recommended to use the IDE to change any of the values in this
+# section.
+# ---------------------------------------------------------------------------
+
+VERSION = BCB.06.00
+# ---------------------------------------------------------------------------
+OBJFILES = .\pcre_compile.obj .\pcre_config.obj .\pcre_dfa_exec.obj \
+  .\pcre_exec.obj .\pcre_fullinfo.obj .\pcre_get.obj .\pcre_globals.obj \
+  .\pcre_info.obj .\pcre_maketables.obj .\pcre_newline.obj \
+  .\pcre_ord2utf8.obj .\pcre_refcount.obj .\pcre_study.obj .\pcre_tables.obj \
+  .\pcre_try_flipped.obj .\pcre_ucd.obj .\pcre_valid_utf8.obj \
+  .\pcre_version.obj .\pcre_xclass.obj .\pcre_default_tables.obj
+
+# ---------------------------------------------------------------------------
+DEBUGLIBPATH = $(BCB)\lib\debug
+RELEASELIBPATH = $(BCB)\lib\release
+USERDEFINES = SUPPORT_UTF8;SUPPORT_UCP
+SYSDEFINES = NO_STRICT;_NO_VCL;_RTLDLL
+INCLUDEPATH = $(pcresrc);$(BCC)\include;$(BCB)\include\vcl
+LIBPATH = $(BCB)\lib\obj;$(BCB)\lib
+# LIBPATH = $(pcresrc)
+WARNINGS= -wpar -w-aus
+PATHC = .;$(pcresrc)
+# PATHOBJ = .;$(LIBPATH)
+ALLLIB = import32.lib cw32i.lib
+# ---------------------------------------------------------------------------
+CFLAG1 = -O2 -Ve -X- -a8 -$(CPU) -b -d -k- -vi -tWM- -DHAVE_CONFIG_H
+
+LFLAGS = -D"" -ap -Tpe -x -Gn
+# ---------------------------------------------------------------------------
+# MAKE SECTION
+# ---------------------------------------------------------------------------
+# This section of the project file is not used by the BCB IDE.  It is for
+# the benefit of building from the command-line using the MAKE utility.
+# ---------------------------------------------------------------------------
+
+.autodepend
+# ---------------------------------------------------------------------------
+
+!if !$d(BCC32)
+BCC32 = bcc32
+!endif
+
+!if !$d(LINKER)
+LINKER = ilink32
+!endif
+
+# ---------------------------------------------------------------------------
+!if $d(PATHC)
+.PATH.C   = $(PATHC)
+!endif
+
+# ---------------------------------------------------------------------------
+pcre: includes tables $(OBJFILES)
+
+# ---------------------------------------------------------------------------
+.c.obj:
+    $(BCC)\BIN\$(BCC32) -c $(CFLAG1) $(CallingConvention) $(WARNINGS) -I$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -n$(@D) {$< }
+
+includes:
+    copy /Y $(pcresrc)\pcre.h.generic $(pcresrc)\pcre.h
+    copy /Y $(pcresrc)\config.h.generic $(pcresrc)\config.h
+
+tables:
+    $(BCC)\BIN\$(BCC32) -c -tWC $(CFLAG1) $(WARNINGS) -I$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -n.\ $(pcresrc)\dftables.c
+    $(BCC)\BIN\$(LINKER) $(LFLAGS) -L$(LIBPATH) c0x32.obj .\dftables.obj, .\dftables.exe,, $(ALLLIB),,
+    del dftables.tds
+    del dftables.obj
+    dftables.exe pcre_default_tables.c
+    del dftables.exe
+# ---------------------------------------------------------------------------
+
+
+
+

BIN
pcre/pcre_compile.obj


BIN
pcre/pcre_config.obj


BIN
pcre/pcre_default_tables.obj


BIN
pcre/pcre_dfa_exec.obj


BIN
pcre/pcre_exec.obj


BIN
pcre/pcre_fullinfo.obj


BIN
pcre/pcre_get.obj


BIN
pcre/pcre_globals.obj


BIN
pcre/pcre_info.obj


BIN
pcre/pcre_maketables.obj


BIN
pcre/pcre_newline.obj


BIN
pcre/pcre_ord2utf8.obj


BIN
pcre/pcre_refcount.obj


BIN
pcre/pcre_study.obj


BIN
pcre/pcre_tables.obj


BIN
pcre/pcre_try_flipped.obj


BIN
pcre/pcre_ucd.obj


BIN
pcre/pcre_valid_utf8.obj


BIN
pcre/pcre_version.obj


BIN
pcre/pcre_xclass.obj