Browse Source

--- Merging r15349 into '.':
U packages/fcl-db/tests/testfieldtypes.pas
U packages/fcl-db/src/sqldb/sqlite/sqlite3conn.pp
--- Merging r15351 into '.':
U packages/fcl-web/src/base/custcgi.pp
--- Merging r15352 into '.':
U packages/fcl-web/src/base/fpapache.pp
U packages/fcl-web/src/base/custweb.pp
U packages/fcl-web/src/base/httpdefs.pp
--- Merging r15360 into '.':
A packages/fcl-web/src/jsonrpc
A packages/fcl-web/src/jsonrpc/fpextdirect.pp
A packages/fcl-web/src/jsonrpc/fpjsonrpc.pp
A packages/fcl-web/src/jsonrpc/Makefile.fpc
A packages/fcl-web/src/jsonrpc/webjsonrpc.pp
A packages/fcl-web/src/jsonrpc/readme.txt
A packages/fcl-web/src/jsonrpc/Makefile
--- Merging r15361 into '.':
U packages/fcl-web/fpmake.pp
U packages/fcl-web/Makefile.fpc
U packages/fcl-web/src/base/fphttp.pp
C packages/fcl-web/Makefile
A packages/fcl-web/examples/jsonrpc
A packages/fcl-web/examples/jsonrpc/demo1
A packages/fcl-web/examples/jsonrpc/demo1/demo.lpr
A packages/fcl-web/examples/jsonrpc/demo1/extdirect.in
A packages/fcl-web/examples/jsonrpc/demo1/echo.in
A packages/fcl-web/examples/jsonrpc/demo1/echobatch.in
A packages/fcl-web/examples/jsonrpc/demo1/wmdemo.lfm
A packages/fcl-web/examples/jsonrpc/demo1/wmdemo.pp
A packages/fcl-web/examples/jsonrpc/demo1/demo.lpi
A packages/fcl-web/examples/jsonrpc/demo1/wmdemo.lrs
A packages/fcl-web/examples/jsonrpc/demo1/notification.in
--- Merging r15362 into '.':
A packages/fcl-web/examples/jsonrpc/demo1/README.txt
--- Merging r15373 into '.':
U packages/fcl-db/tests/toolsunit.pas
U packages/fcl-db/tests/testdbbasics.pas
U packages/fcl-db/tests/testbufdatasetstreams.pas
--- Merging r15378 into '.':
U packages/fcl-web/src/jsonrpc/fpjsonrpc.pp
--- Merging r15379 into '.':
U packages/fcl-web/src/jsonrpc/fpextdirect.pp
--- Merging r15380 into '.':
U packages/fcl-web/src/jsonrpc/webjsonrpc.pp
--- Merging r15381 into '.':
G packages/fcl-web/src/jsonrpc/fpextdirect.pp
G packages/fcl-web/src/jsonrpc/webjsonrpc.pp
--- Merging r15388 into '.':
G packages/fcl-db/tests/toolsunit.pas
U packages/fcl-db/tests/dbtestframework.pas
G packages/fcl-db/tests/testfieldtypes.pas
--- Merging r15389 into '.':
G packages/fcl-db/tests/toolsunit.pas
--- Merging r15392 into '.':
G packages/fcl-db/tests/testdbbasics.pas
--- Merging r15393 into '.':
G packages/fcl-db/tests/testdbbasics.pas
U packages/fcl-db/tests/sqldbtoolsunit.pas
U packages/fcl-db/src/sqldb/sqldb.pp
U packages/fcl-db/src/base/dataset.inc
U packages/fcl-db/src/base/bufdataset.pas
--- Merging r15394 into '.':
G packages/fcl-db/tests/testdbbasics.pas
Summary of conflicts:
Text conflicts: 1

# revisions: 15349,15351,15352,15360,15361,15362,15373,5375,15378,15379,15380,15381,15388,15389,15392,15393,15394
------------------------------------------------------------------------
r15349 | joost | 2010-05-30 22:09:48 +0200 (Sun, 30 May 2010) | 1 line
Changed paths:
M /trunk/packages/fcl-db/src/sqldb/sqlite/sqlite3conn.pp
M /trunk/packages/fcl-db/tests/testfieldtypes.pas

* Do not use an old connection handle when the connection is reconnected, bug #16438 + test
------------------------------------------------------------------------
------------------------------------------------------------------------
r15351 | michael | 2010-05-31 09:05:36 +0200 (Mon, 31 May 2010) | 1 line
Changed paths:
M /trunk/packages/fcl-web/src/base/custcgi.pp

* Empty pathinfo
------------------------------------------------------------------------
------------------------------------------------------------------------
r15352 | michael | 2010-05-31 09:08:06 +0200 (Mon, 31 May 2010) | 1 line
Changed paths:
M /trunk/packages/fcl-web/src/base/custweb.pp
M /trunk/packages/fcl-web/src/base/fpapache.pp
M /trunk/packages/fcl-web/src/base/httpdefs.pp

* reverted ProcessedPathInfo to ReturnedPathInfo
------------------------------------------------------------------------
------------------------------------------------------------------------
r15360 | michael | 2010-06-03 17:09:29 +0200 (Thu, 03 Jun 2010) | 1 line
Changed paths:
A /trunk/packages/fcl-web/src/jsonrpc
A /trunk/packages/fcl-web/src/jsonrpc/Makefile
A /trunk/packages/fcl-web/src/jsonrpc/Makefile.fpc
A /trunk/packages/fcl-web/src/jsonrpc/fpextdirect.pp
A /trunk/packages/fcl-web/src/jsonrpc/fpjsonrpc.pp
A /trunk/packages/fcl-web/src/jsonrpc/readme.txt
A /trunk/packages/fcl-web/src/jsonrpc/webjsonrpc.pp

* Added JSON-RPC functionality
------------------------------------------------------------------------
------------------------------------------------------------------------
r15361 | michael | 2010-06-03 17:18:09 +0200 (Thu, 03 Jun 2010) | 1 line
Changed paths:
M /trunk/packages/fcl-web/Makefile
M /trunk/packages/fcl-web/Makefile.fpc
A /trunk/packages/fcl-web/examples/jsonrpc
A /trunk/packages/fcl-web/examples/jsonrpc/demo1
A /trunk/packages/fcl-web/examples/jsonrpc/demo1/demo.lpi
A /trunk/packages/fcl-web/examples/jsonrpc/demo1/demo.lpr
A /trunk/packages/fcl-web/examples/jsonrpc/demo1/echo.in
A /trunk/packages/fcl-web/examples/jsonrpc/demo1/echobatch.in
A /trunk/packages/fcl-web/examples/jsonrpc/demo1/extdirect.in
A /trunk/packages/fcl-web/examples/jsonrpc/demo1/notification.in
A /trunk/packages/fcl-web/examples/jsonrpc/demo1/wmdemo.lfm
A /trunk/packages/fcl-web/examples/jsonrpc/demo1/wmdemo.lrs
A /trunk/packages/fcl-web/examples/jsonrpc/demo1/wmdemo.pp
M /trunk/packages/fcl-web/fpmake.pp
M /trunk/packages/fcl-web/src/base/fphttp.pp

* json-rpc examples, and compile it as well
------------------------------------------------------------------------
------------------------------------------------------------------------
r15362 | michael | 2010-06-03 17:23:47 +0200 (Thu, 03 Jun 2010) | 1 line
Changed paths:
A /trunk/packages/fcl-web/examples/jsonrpc/demo1/README.txt

* Added some test instructions
------------------------------------------------------------------------
------------------------------------------------------------------------
r15373 | joost | 2010-06-04 12:44:08 +0200 (Fri, 04 Jun 2010) | 1 line
Changed paths:
M /trunk/packages/fcl-db/tests/testbufdatasetstreams.pas
M /trunk/packages/fcl-db/tests/testdbbasics.pas
M /trunk/packages/fcl-db/tests/toolsunit.pas

* Moved TDBBasicsTestSetup to toolsunit
------------------------------------------------------------------------
------------------------------------------------------------------------
r5375 | yury | 2006-11-14 17:49:02 +0100 (Tue, 14 Nov 2006) | 1 line
Changed paths:
M /trunk/compiler/arm/aasmcpu.pas
M /trunk/compiler/pmodules.pas

* fix arm-wince exception handling.
------------------------------------------------------------------------
------------------------------------------------------------------------
r15378 | michael | 2010-06-04 17:47:29 +0200 (Fri, 04 Jun 2010) | 1 line
Changed paths:
M /trunk/packages/fcl-web/src/jsonrpc/fpjsonrpc.pp

* Implemented TPAramDef.Assign, or streaming does not work correctly
------------------------------------------------------------------------
------------------------------------------------------------------------
r15379 | michael | 2010-06-04 17:48:26 +0200 (Fri, 04 Jun 2010) | 1 line
Changed paths:
M /trunk/packages/fcl-web/src/jsonrpc/fpextdirect.pp

* Fixed 2 bugs and added debug directive
------------------------------------------------------------------------
------------------------------------------------------------------------
r15380 | michael | 2010-06-04 17:48:54 +0200 (Fri, 04 Jun 2010) | 1 line
Changed paths:
M /trunk/packages/fcl-web/src/jsonrpc/webjsonrpc.pp

* Added debug directive for easier debugging
------------------------------------------------------------------------
------------------------------------------------------------------------
r15381 | michael | 2010-06-04 17:49:20 +0200 (Fri, 04 Jun 2010) | 1 line
Changed paths:
M /trunk/packages/fcl-web/src/jsonrpc/fpextdirect.pp
M /trunk/packages/fcl-web/src/jsonrpc/webjsonrpc.pp

* Debugging by default disabled
------------------------------------------------------------------------
------------------------------------------------------------------------
r15388 | joost | 2010-06-05 18:24:53 +0200 (Sat, 05 Jun 2010) | 1 line
Changed paths:
M /trunk/packages/fcl-db/tests/dbtestframework.pas
M /trunk/packages/fcl-db/tests/testfieldtypes.pas
M /trunk/packages/fcl-db/tests/toolsunit.pas

* Make the dbconnector ref-counted
------------------------------------------------------------------------
------------------------------------------------------------------------
r15389 | joost | 2010-06-05 18:39:53 +0200 (Sat, 05 Jun 2010) | 1 line
Changed paths:
M /trunk/packages/fcl-db/tests/toolsunit.pas

* Added TDBConnector.TestUniDirectional property
------------------------------------------------------------------------
------------------------------------------------------------------------
r15392 | joost | 2010-06-05 21:04:58 +0200 (Sat, 05 Jun 2010) | 1 line
Changed paths:
M /trunk/packages/fcl-db/tests/testdbbasics.pas

* Fixed mem-leaks in tests
------------------------------------------------------------------------
------------------------------------------------------------------------
r15393 | joost | 2010-06-05 22:05:18 +0200 (Sat, 05 Jun 2010) | 2 lines
Changed paths:
M /trunk/packages/fcl-db/src/base/bufdataset.pas
M /trunk/packages/fcl-db/src/base/dataset.inc
M /trunk/packages/fcl-db/src/sqldb/sqldb.pp
M /trunk/packages/fcl-db/tests/sqldbtoolsunit.pas
M /trunk/packages/fcl-db/tests/testdbbasics.pas

* Implemented TBufDataset.UniDirectional property
* Run all tests of TestDBBasics also using UniDirectional TBufDatasets. (Introduces a lot of false failures)
------------------------------------------------------------------------
------------------------------------------------------------------------
r15394 | joost | 2010-06-05 23:13:36 +0200 (Sat, 05 Jun 2010) | 1 line
Changed paths:
M /trunk/packages/fcl-db/tests/testdbbasics.pas

* Remove the FileName property from the datasets after the test
------------------------------------------------------------------------

git-svn-id: branches/fixes_2_4@16386 -

marco 14 years ago
parent
commit
2bd0853001
35 changed files with 5896 additions and 248 deletions
  1. 16 0
      .gitattributes
  2. 264 16
      packages/fcl-db/src/base/bufdataset.pas
  3. 1 1
      packages/fcl-db/src/base/dataset.inc
  4. 3 3
      packages/fcl-db/src/sqldb/sqldb.pp
  5. 2 1
      packages/fcl-db/src/sqldb/sqlite/sqlite3conn.pp
  6. 1 0
      packages/fcl-db/tests/dbtestframework.pas
  7. 19 3
      packages/fcl-db/tests/sqldbtoolsunit.pas
  8. 0 19
      packages/fcl-db/tests/testbufdatasetstreams.pas
  9. 88 65
      packages/fcl-db/tests/testdbbasics.pas
  10. 17 1
      packages/fcl-db/tests/testfieldtypes.pas
  11. 46 1
      packages/fcl-db/tests/toolsunit.pas
  12. 164 122
      packages/fcl-web/Makefile
  13. 1 1
      packages/fcl-web/Makefile.fpc
  14. 21 0
      packages/fcl-web/examples/jsonrpc/demo1/README.txt
  15. 93 0
      packages/fcl-web/examples/jsonrpc/demo1/demo.lpi
  16. 16 0
      packages/fcl-web/examples/jsonrpc/demo1/demo.lpr
  17. 1 0
      packages/fcl-web/examples/jsonrpc/demo1/echo.in
  18. 4 0
      packages/fcl-web/examples/jsonrpc/demo1/echobatch.in
  19. 4 0
      packages/fcl-web/examples/jsonrpc/demo1/extdirect.in
  20. 1 0
      packages/fcl-web/examples/jsonrpc/demo1/notification.in
  21. 46 0
      packages/fcl-web/examples/jsonrpc/demo1/wmdemo.lfm
  22. 15 0
      packages/fcl-web/examples/jsonrpc/demo1/wmdemo.lrs
  23. 383 0
      packages/fcl-web/examples/jsonrpc/demo1/wmdemo.pp
  24. 14 1
      packages/fcl-web/fpmake.pp
  25. 1 5
      packages/fcl-web/src/base/custcgi.pp
  26. 1 1
      packages/fcl-web/src/base/custweb.pp
  27. 1 1
      packages/fcl-web/src/base/fpapache.pp
  28. 1 0
      packages/fcl-web/src/base/fphttp.pp
  29. 7 7
      packages/fcl-web/src/base/httpdefs.pp
  30. 2455 0
      packages/fcl-web/src/jsonrpc/Makefile
  31. 26 0
      packages/fcl-web/src/jsonrpc/Makefile.fpc
  32. 430 0
      packages/fcl-web/src/jsonrpc/fpextdirect.pp
  33. 1249 0
      packages/fcl-web/src/jsonrpc/fpjsonrpc.pp
  34. 189 0
      packages/fcl-web/src/jsonrpc/readme.txt
  35. 316 0
      packages/fcl-web/src/jsonrpc/webjsonrpc.pp

+ 16 - 0
.gitattributes

@@ -1648,6 +1648,16 @@ packages/fcl-res/xml/versiontypes.xml svneol=native#text/plain
 packages/fcl-res/xml/winpeimagereader.xml svneol=native#text/plain
 packages/fcl-web/Makefile svneol=native#text/plain
 packages/fcl-web/Makefile.fpc svneol=native#text/plain
+packages/fcl-web/examples/jsonrpc/demo1/README.txt svneol=native#text/plain
+packages/fcl-web/examples/jsonrpc/demo1/demo.lpi svneol=native#text/plain
+packages/fcl-web/examples/jsonrpc/demo1/demo.lpr svneol=native#text/plain
+packages/fcl-web/examples/jsonrpc/demo1/echo.in svneol=native#text/plain
+packages/fcl-web/examples/jsonrpc/demo1/echobatch.in svneol=native#text/plain
+packages/fcl-web/examples/jsonrpc/demo1/extdirect.in svneol=native#text/plain
+packages/fcl-web/examples/jsonrpc/demo1/notification.in svneol=native#text/plain
+packages/fcl-web/examples/jsonrpc/demo1/wmdemo.lfm svneol=native#text/plain
+packages/fcl-web/examples/jsonrpc/demo1/wmdemo.lrs svneol=native#text/plain
+packages/fcl-web/examples/jsonrpc/demo1/wmdemo.pp svneol=native#text/plain
 packages/fcl-web/examples/webdata/demo/createusers.lpi svneol=native#text/plain
 packages/fcl-web/examples/webdata/demo/createusers.lpr svneol=native#text/plain
 packages/fcl-web/examples/webdata/demo/extgrid-json.html svneol=native#text/plain
@@ -1740,6 +1750,12 @@ packages/fcl-web/src/base/httpdefs.pp svneol=native#text/plain
 packages/fcl-web/src/base/webpage.pp svneol=native#text/plain
 packages/fcl-web/src/base/websession.pp svneol=native#text/plain
 packages/fcl-web/src/base/webutil.pp svneol=native#text/plain
+packages/fcl-web/src/jsonrpc/Makefile svneol=native#text/plain
+packages/fcl-web/src/jsonrpc/Makefile.fpc svneol=native#text/plain
+packages/fcl-web/src/jsonrpc/fpextdirect.pp svneol=native#text/plain
+packages/fcl-web/src/jsonrpc/fpjsonrpc.pp svneol=native#text/plain
+packages/fcl-web/src/jsonrpc/readme.txt svneol=native#text/plain
+packages/fcl-web/src/jsonrpc/webjsonrpc.pp svneol=native#text/plain
 packages/fcl-web/src/webdata/Makefile svneol=native#text/plain
 packages/fcl-web/src/webdata/Makefile.fpc svneol=native#text/plain
 packages/fcl-web/src/webdata/extjsjson.pp svneol=native#text/plain

+ 264 - 16
packages/fcl-db/src/base/bufdataset.pas

@@ -157,7 +157,7 @@ type
     procedure BeginUpdate; virtual; abstract;
     // Adds a record to the end of the index as the new last record (spare record)
     // Normally only used in GetNextPacket
-    procedure AddRecord(Const ARecord : PChar); virtual; abstract;
+    procedure AddRecord; virtual; abstract;
     // Inserts a record before the current record, or if the record is sorted,
     // insert it to the proper position
     procedure InsertRecordBeforeCurrentRecord(Const ARecord : PChar); virtual; abstract;
@@ -226,11 +226,57 @@ type
     Function GetRecNo(const ABookmark : PBufBookmark) : integer; override;
 
     procedure BeginUpdate; override;
-    procedure AddRecord(Const ARecord : PChar); override;
+    procedure AddRecord; override;
     procedure InsertRecordBeforeCurrentRecord(Const ARecord : PChar); override;
     procedure EndUpdate; override;
   end;
 
+  { TUniDirectionalBufIndex }
+
+  TUniDirectionalBufIndex = class(TBufIndex)
+  private
+    FSPareBuffer: PChar;
+  protected
+    function GetBookmarkSize: integer; override;
+    function GetCurrentBuffer: Pointer; override;
+    function GetCurrentRecord: PChar; override;
+    function GetIsInitialized: boolean; override;
+    function GetSpareBuffer: PChar; override;
+    function GetSpareRecord: PChar; override;
+  public
+    function ScrollBackward : TGetResult; override;
+    function ScrollForward : TGetResult; override;
+    function GetCurrent : TGetResult; override;
+    function ScrollFirst : TGetResult; override;
+    procedure ScrollLast; override;
+
+    procedure SetToFirstRecord; override;
+    procedure SetToLastRecord; override;
+
+    procedure StoreCurrentRecord; override;
+    procedure RestoreCurrentRecord; override;
+
+    function CanScrollForward : Boolean; override;
+    procedure DoScrollForward; override;
+
+    procedure StoreCurrentRecIntoBookmark(const ABookmark: PBufBookmark); override;
+    procedure StoreSpareRecIntoBookmark(const ABookmark: PBufBookmark); override;
+    procedure GotoBookmark(const ABookmark : PBufBookmark); override;
+
+    procedure InitialiseIndex; override;
+    procedure InitialiseSpareRecord(const ASpareRecord : PChar); override;
+    procedure ReleaseSpareRecord; override;
+
+    procedure RemoveRecordFromIndex(const ABookmark : TBufBookmark); override;
+    Function GetRecNo(const ABookmark : PBufBookmark) : integer; override;
+
+    procedure BeginUpdate; override;
+    procedure AddRecord; override;
+    procedure InsertRecordBeforeCurrentRecord(Const ARecord : PChar); override;
+    procedure EndUpdate; override;
+  end;
+
+
   { TArrayBufIndex }
 
   TArrayBufIndex = class(TBufIndex)
@@ -282,7 +328,7 @@ type
     procedure InsertRecordBeforeCurrentRecord(Const ARecord : PChar); override;
 
     procedure BeginUpdate; override;
-    procedure AddRecord(Const ARecord : PChar); override;
+    procedure AddRecord; override;
     procedure EndUpdate; override;
   end;
 
@@ -387,6 +433,7 @@ type
     procedure CalcRecordSize;
     function GetIndexFieldNames: String;
     function GetIndexName: String;
+    function GetBufUniDirectional: boolean;
     function LoadBuffer(Buffer : PChar): TGetResult;
     function GetFieldSize(FieldDef : TFieldDef) : longint;
     function GetRecordUpdateBuffer(const ABookmark : TBufBookmark; IncludePrior : boolean = false; AFindNext : boolean = false) : boolean;
@@ -403,6 +450,8 @@ type
     procedure IntLoadFielddefsFromFile;
     procedure IntLoadRecordsFromFile;
     procedure CurrentRecordToBuffer(Buffer: PChar);
+    procedure SetBufUniDirectional(const AValue: boolean);
+    procedure InitDefaultIndexes;
   protected
     procedure UpdateIndexDefs; override;
     function GetNewBlobBuffer : PBlobBuffer;
@@ -486,6 +535,7 @@ type
     property IndexDefs : TIndexDefs read GetIndexDefs;
     property IndexName : String read GetIndexName write SetIndexName;
     property IndexFieldNames : String read GetIndexFieldNames write SetIndexFieldNames;
+    property UniDirectional: boolean read GetBufUniDirectional write SetBufUniDirectional;
   end;
 
   TBufDataset = class(TCustomBufDataset)
@@ -658,16 +708,12 @@ begin
   Inherited Create(AOwner);
   FMaxIndexesCount:=2;
   FIndexesCount:=0;
-  InternalAddIndex('DEFAULT_ORDER','',[],'','');
-  FCurrentIndex:=FIndexes[0];
-  InternalAddIndex('','',[],'','');
 
   FIndexDefs := TIndexDefs.Create(Self);
 
   SetLength(FUpdateBuffer,0);
   SetLength(FBlobBuffers,0);
   SetLength(FUpdateBlobBuffers,0);
-  BookmarkSize := FCurrentIndex.BookmarkSize;
   FParser := nil;
   FPacketRecords := 10;
 end;
@@ -1010,6 +1056,7 @@ procedure TCustomBufDataset.InternalOpen;
 var IndexNr : integer;
 
 begin
+  InitDefaultIndexes;
   if not Assigned(FDatasetReader) and (FileName<>'') then
     begin
     FFileStream := TFileStream.Create(FileName,fmOpenRead);
@@ -1046,7 +1093,7 @@ var r  : integer;
 
 begin
   FOpen:=False;
-  with FIndexes[0] do if IsInitialized then
+  if FIndexesCount>0 then with FIndexes[0] do if IsInitialized then
     begin
     iGetResult:=ScrollFirst;
     while iGetResult = grOK do
@@ -1306,8 +1353,10 @@ begin
     FCursOnFirstRec := False;
 end;
 
-procedure TDoubleLinkedBufIndex.AddRecord(Const ARecord : PChar);
+procedure TDoubleLinkedBufIndex.AddRecord;
+var ARecord: PChar;
 begin
+  ARecord := FDataset.IntAllocRecordBuffer;
   FLastRecBuf[IndNr].next := pointer(ARecord);
   FLastRecBuf[IndNr].next[IndNr].prior := FLastRecBuf;
 
@@ -1351,6 +1400,30 @@ begin
   GetCalcFields(Buffer);
 end;
 
+procedure TCustomBufDataset.SetBufUniDirectional(const AValue: boolean);
+begin
+  CheckInactive;
+  if (AValue<>IsUniDirectional) then
+    begin
+    SetUniDirectional(AValue);
+    SetLength(FIndexes,0);
+    FPacketRecords := 1; // temporary
+    FIndexesCount:=0;
+    end;
+end;
+
+procedure TCustomBufDataset.InitDefaultIndexes;
+begin
+  if FIndexesCount=0 then
+    begin
+    InternalAddIndex('DEFAULT_ORDER','',[],'','');
+    FCurrentIndex:=FIndexes[0];
+    if not IsUniDirectional then
+      InternalAddIndex('','',[],'','');
+    BookmarkSize := FCurrentIndex.BookmarkSize;
+    end;
+end;
+
 function TCustomBufDataset.GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult;
 
 var Acceptable : Boolean;
@@ -1446,6 +1519,8 @@ procedure TCustomBufDataset.SetIndexFieldNames(const AValue: String);
 begin
   if AValue<>'' then
     begin
+    if FIndexesCount=0 then
+      InitDefaultIndexes;
     FIndexes[1].FieldsName:=AValue;
     FCurrentIndex:=FIndexes[1];
     if active then
@@ -1534,7 +1609,7 @@ begin
     begin
     with FIndexes[0] do
       begin
-      AddRecord(IntAllocRecordBuffer);
+      AddRecord;
       pb := SpareBuffer;
       end;
     inc(i);
@@ -2131,6 +2206,11 @@ begin
   result := FCurrentIndex.Name;
 end;
 
+function TCustomBufDataset.GetBufUniDirectional: boolean;
+begin
+  result := IsUniDirectional;
+end;
+
 function TCustomBufDataset.GetRecordSize : Word;
 
 begin
@@ -2345,7 +2425,11 @@ end;
 procedure TCustomBufDataset.AddIndex(const AName, AFields : string; AOptions : TIndexOptions; const ADescFields: string = '';
                                const ACaseInsFields: string = '');
 begin
+  CheckBiDirectional;
   if AFields='' then DatabaseError(SNoIndexFieldNameGiven);
+
+  if FIndexesCount=0 then
+    InitDefaultIndexes;
   
   if active and (FIndexesCount=FMaxIndexesCount) then
     DatabaseError(SMaxIndexes);
@@ -2485,6 +2569,7 @@ procedure TCustomBufDataset.LoadFromStream(AStream: TStream; Format: TDataPacket
 var APacketReaderReg : TDatapacketReaderRegistration;
     APacketReader : TDataPacketReader;
 begin
+  CheckBiDirectional;
   if GetRegisterDatapacketReader(AStream,format,APacketReaderReg) then
     APacketReader := APacketReaderReg.ReaderClass.create(AStream)
   else if TFpcBinaryDatapacketReader.RecognizeStream(AStream) then
@@ -2505,6 +2590,7 @@ procedure TCustomBufDataset.SaveToStream(AStream: TStream; Format: TDataPacketFo
 var APacketReaderReg : TDatapacketReaderRegistration;
     APacketWriter : TDataPacketReader;
 begin
+  CheckBiDirectional;
   if GetRegisterDatapacketReader(Nil,format,APacketReaderReg) then
     APacketWriter := APacketReaderReg.ReaderClass.create(AStream)
   else if Format = dfBinary then
@@ -2538,7 +2624,7 @@ end;
 
 function TCustomBufDataset.BookmarkValid(ABookmark: TBookmark): Boolean;
 begin
-  Result:=FCurrentIndex.BookmarkValid(ABookmark);
+  Result:=assigned(FCurrentIndex) and  FCurrentIndex.BookmarkValid(ABookmark);
 end;
 
 function TCustomBufDataset.CompareBookmarks(Bookmark1, Bookmark2: TBookmark
@@ -2566,6 +2652,7 @@ var StoreState      : TDataSetState;
     x               : integer;
 
 begin
+  CheckBiDirectional;
   FDatasetReader.InitLoadRecords;
   StoreState:=SetTempState(dsFilter);
 
@@ -2598,7 +2685,7 @@ begin
       fillchar(FFilterBuffer^,FNullmaskSize,0);
 
       FDatasetReader.RestoreRecord(self);
-      FIndexes[0].AddRecord(IntAllocRecordBuffer);
+      FIndexes[0].AddRecord;
       inc(FBRecordCount);
 
       AddRecordBuffer:=False;
@@ -2618,7 +2705,7 @@ begin
 
       FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind:= ukDelete;
       FIndexes[0].StoreSpareRecIntoBookmark(@FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData);
-      FIndexes[0].AddRecord(IntAllocRecordBuffer);
+      FIndexes[0].AddRecord;
       FIndexes[0].RemoveRecordFromIndex(FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData);
       FIndexes[0].StoreSpareRecIntoBookmark(@FUpdateBuffer[FCurrentUpdateBuffer].NextBookmarkData);
 
@@ -2647,7 +2734,7 @@ begin
         FCurrentIndex.StoreSpareRecIntoBookmark(@FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData);
         end;
 
-      FIndexes[0].AddRecord(IntAllocRecordBuffer);
+      FIndexes[0].AddRecord;
       inc(FBRecordCount);
       end;
 
@@ -2676,7 +2763,10 @@ begin
   inc(FIndexesCount);
   setlength(FIndexes,FIndexesCount); // This invalidates the currentindex! -> not anymore
   FCurrentIndex:=FIndexes[StoreIndNr];
-  FIndexes[FIndexesCount-1] := TDoubleLinkedBufIndex.Create(self);
+  if IsUniDirectional then
+    FIndexes[FIndexesCount-1] := TUniDirectionalBufIndex.Create(self)
+  else
+    FIndexes[FIndexesCount-1] := TDoubleLinkedBufIndex.Create(self);
 //  FIndexes[FIndexesCount-1] := TArrayBufIndex.Create(self);
   FIndexes[FIndexesCount-1].InitialiseIndex;
   with (FIndexes[FIndexesCount-1] as TBufIndex) do
@@ -3093,8 +3183,10 @@ begin
 //  inherited BeginUpdate;
 end;
 
-procedure TArrayBufIndex.AddRecord(const ARecord: PChar);
+procedure TArrayBufIndex.AddRecord;
+var ARecord: PChar;
 begin
+  ARecord := FDataset.IntAllocRecordBuffer;
   inc(FLastRecInd);
   if FLastRecInd >= length(FRecordArray) then
     SetLength(FRecordArray,length(FRecordArray)+FGrowBuffer);
@@ -3243,6 +3335,162 @@ begin
     Result := False;
 end;
 
+{ TUniDirectionalBufIndex }
+
+function TUniDirectionalBufIndex.GetBookmarkSize: integer;
+begin
+  // In principle there are no bookmarks, and the size should be 0.
+  // But there is quite some code in TCustomBufDataset that relies on
+  // an existing bookmark of the TBufBookmark type.
+  // This code could be moved to the TBufIndex but that would make things
+  // more complicated and probably slower. So use a 'fake' bookmark of
+  // size TBufBookmark.
+  // When there are other TBufIndexes which also need special bookmark-code
+  // this can be adapted.
+  Result:=sizeof(TBufBookmark);
+end;
+
+function TUniDirectionalBufIndex.GetCurrentBuffer: Pointer;
+begin
+  result := FSPareBuffer;
+end;
+
+function TUniDirectionalBufIndex.GetCurrentRecord: PChar;
+begin
+//  Result:=inherited GetCurrentRecord;
+end;
+
+function TUniDirectionalBufIndex.GetIsInitialized: boolean;
+begin
+  Result := Assigned(FSPareBuffer);
+end;
+
+function TUniDirectionalBufIndex.GetSpareBuffer: PChar;
+begin
+  result := FSPareBuffer;
+end;
+
+function TUniDirectionalBufIndex.GetSpareRecord: PChar;
+begin
+  result := FSPareBuffer;
+end;
+
+function TUniDirectionalBufIndex.ScrollBackward: TGetResult;
+begin
+  result := grError;
+end;
+
+function TUniDirectionalBufIndex.ScrollForward: TGetResult;
+begin
+  result := grOk;
+end;
+
+function TUniDirectionalBufIndex.GetCurrent: TGetResult;
+begin
+  result := grOk;
+end;
+
+function TUniDirectionalBufIndex.ScrollFirst: TGetResult;
+begin
+  Result:=grError;
+end;
+
+procedure TUniDirectionalBufIndex.ScrollLast;
+begin
+  DatabaseError(SUniDirectional);
+end;
+
+procedure TUniDirectionalBufIndex.SetToFirstRecord;
+begin
+  DatabaseError(SUniDirectional);
+end;
+
+procedure TUniDirectionalBufIndex.SetToLastRecord;
+begin
+  DatabaseError(SUniDirectional);
+end;
+
+procedure TUniDirectionalBufIndex.StoreCurrentRecord;
+begin
+  DatabaseError(SUniDirectional);
+end;
+
+procedure TUniDirectionalBufIndex.RestoreCurrentRecord;
+begin
+  DatabaseError(SUniDirectional);
+end;
+
+function TUniDirectionalBufIndex.CanScrollForward: Boolean;
+begin
+  // should return true if a next record is already fetched
+  result := false;
+end;
+
+procedure TUniDirectionalBufIndex.DoScrollForward;
+begin
+  // do nothing
+end;
+
+procedure TUniDirectionalBufIndex.StoreCurrentRecIntoBookmark(const ABookmark: PBufBookmark);
+begin
+  // do nothing
+end;
+
+procedure TUniDirectionalBufIndex.StoreSpareRecIntoBookmark(const ABookmark: PBufBookmark);
+begin
+  // do nothing
+end;
+
+procedure TUniDirectionalBufIndex.GotoBookmark(const ABookmark: PBufBookmark);
+begin
+  DatabaseError(SUniDirectional);
+end;
+
+procedure TUniDirectionalBufIndex.InitialiseIndex;
+begin
+  // do nothing
+end;
+
+procedure TUniDirectionalBufIndex.InitialiseSpareRecord(const ASpareRecord: PChar);
+begin
+  FSPareBuffer:=ASpareRecord;
+end;
+
+procedure TUniDirectionalBufIndex.ReleaseSpareRecord;
+begin
+  FSPareBuffer:=nil;
+end;
+
+procedure TUniDirectionalBufIndex.RemoveRecordFromIndex(const ABookmark: TBufBookmark);
+begin
+  DatabaseError(SUniDirectional);
+end;
+
+function TUniDirectionalBufIndex.GetRecNo(const ABookmark: PBufBookmark): integer;
+begin
+  result := -1;
+end;
+
+procedure TUniDirectionalBufIndex.BeginUpdate;
+begin
+  // Do nothing
+end;
+
+procedure TUniDirectionalBufIndex.AddRecord;
+begin
+  // Do nothing
+end;
+
+procedure TUniDirectionalBufIndex.InsertRecordBeforeCurrentRecord(const ARecord: PChar);
+begin
+  // Do nothing
+end;
+
+procedure TUniDirectionalBufIndex.EndUpdate;
+begin
+  // Do nothing
+end;
+
 initialization
   setlength(RegisteredDatapacketReaders,0);
 finalization

+ 1 - 1
packages/fcl-db/src/base/dataset.inc

@@ -1133,7 +1133,7 @@ begin
   Writeln('Getting next buffers');
 {$endif}
   GetNextRecords;
-  if FRecordCount < FBufferCount then
+  if (FRecordCount < FBufferCount) and not IsUniDirectional then
     begin
     FActiveRecord := FActiveRecord + GetPriorRecords;
     CursorPosChanged;

+ 3 - 3
packages/fcl-db/src/sqldb/sqldb.pp

@@ -1244,7 +1244,7 @@ begin
       // Call UpdateServerIndexDefs before Execute, to avoid problems with connections
       // which do not allow processing multiple recordsets at a time. (Microsoft
       // calls this MARS, see bug 13241)
-      if DefaultFields and FUpdateable and FusePrimaryKeyAsKey then
+      if DefaultFields and FUpdateable and FusePrimaryKeyAsKey and (not IsUniDirectional) then
         UpdateServerIndexDefs;
       Execute;
       // InternalInitFieldDef is only called after a prepare. i.e. not twice if
@@ -1254,7 +1254,7 @@ begin
         begin
         CreateFields;
 
-        if FUpdateable then
+        if FUpdateable and (not IsUniDirectional) then
           begin
           if FusePrimaryKeyAsKey then
             begin
@@ -1555,7 +1555,7 @@ Function TCustomSQLQuery.GetCanModify: Boolean;
 begin
   // the test for assigned(FCursor) is needed for the case that the dataset isn't opened
   if assigned(FCursor) and (FCursor.FStatementType = stSelect) then
-    Result:= FUpdateable and (not FReadOnly)
+    Result:= FUpdateable and (not FReadOnly) and (not IsUniDirectional)
   else
     Result := False;
 end;

+ 2 - 1
packages/fcl-db/src/sqldb/sqlite/sqlite3conn.pp

@@ -280,7 +280,6 @@ Var
 
 begin
   Res:= TSQLite3Cursor.create;
-  Res.fhandle:=self.fhandle;
   Result:=Res;
 end;
 
@@ -292,6 +291,7 @@ end;
 procedure TSQLite3Connection.PrepareStatement(cursor: TSQLCursor;
                ATransaction: TSQLTransaction; buf: string; AParams: TParams);
 begin
+  TSQLite3Cursor(cursor).fhandle:=self.fhandle;
   TSQLite3Cursor(cursor).Prepare(Buf,AParams);
 end;
 
@@ -299,6 +299,7 @@ procedure TSQLite3Connection.UnPrepareStatement(cursor: TSQLCursor);
 
 begin
   TSQLite3Cursor(cursor).UnPrepare;
+  TSQLite3Cursor(cursor).fhandle:=nil;
 end;
 
 

+ 1 - 0
packages/fcl-db/tests/dbtestframework.pas

@@ -47,4 +47,5 @@ begin
     FXMLResultsWriter.Free;
     FDigestResultsWriter.Free;
   end;
+  FreeDBConnector;
 end.

+ 19 - 3
packages/fcl-db/tests/sqldbtoolsunit.pas

@@ -63,14 +63,17 @@ const MySQLdbTypes = [mysql40,mysql41,mysql50];
 type
 { TSQLDBConnector }
   TSQLDBConnector = class(TDBConnector)
-    FConnection   : TSQLConnection;
-    FTransaction  : TSQLTransaction;
-    FQuery        : TSQLQuery;
   private
+    FConnection    : TSQLConnection;
+    FTransaction   : TSQLTransaction;
+    FQuery         : TSQLQuery;
+    FUniDirectional: boolean;
     procedure CreateFConnection;
     procedure CreateFTransaction;
     Function CreateQuery : TSQLQuery;
   protected
+    procedure SetTestUniDirectional(const AValue: boolean); override;
+    function GetTestUniDirectional: boolean; override;
     procedure CreateNDatasets; override;
     procedure CreateFieldDataset; override;
     procedure DropNDatasets; override;
@@ -167,6 +170,17 @@ begin
     end;
 end;
 
+procedure TSQLDBConnector.SetTestUniDirectional(const AValue: boolean);
+begin
+  FUniDirectional:=avalue;
+  FQuery.UniDirectional:=AValue;
+end;
+
+function TSQLDBConnector.GetTestUniDirectional: boolean;
+begin
+  result := FUniDirectional;
+end;
+
 procedure TSQLDBConnector.CreateNDatasets;
 var CountID : Integer;
 begin
@@ -273,6 +287,7 @@ begin
     begin
     sql.clear;
     sql.add('SELECT * FROM FPDEV WHERE ID < '+inttostr(n+1));
+    UniDirectional:=TestUniDirectional;
     end;
 end;
 
@@ -283,6 +298,7 @@ begin
     begin
     sql.clear;
     sql.add('SELECT * FROM FPDEV_FIELD');
+    tsqlquery(Result).UniDirectional:=TestUniDirectional;
     end;
 end;
 

+ 0 - 19
packages/fcl-db/tests/testbufdatasetstreams.pas

@@ -67,14 +67,6 @@ type
     procedure TestFileNameProperty;
   end;
 
-  { TSQLTestSetup }
-
-  TDBBasicsTestSetup = class(TTestSetup)
-  protected
-    procedure OneTimeSetup; override;
-    procedure OneTimeTearDown; override;
-  end;
-
 implementation
 
 uses toolsunit, SQLDBToolsUnit, sqldb, XMLDatapacketReader;
@@ -446,17 +438,6 @@ begin
   TestChangesApplyUpdates(@DeleteAllInsertChange, False);
 end;
 
-{ TSQLTestSetup }
-procedure TDBBasicsTestSetup.OneTimeSetup;
-begin
-  InitialiseDBConnector;
-end;
-
-procedure TDBBasicsTestSetup.OneTimeTearDown;
-begin
-  FreeAndNil(DBConnector);
-end;
-
 initialization
   if uppercase(dbconnectorname)='SQL' then
     RegisterTestDecorator(TDBBasicsTestSetup, TTestBufDatasetStreams);

+ 88 - 65
packages/fcl-db/tests/testdbbasics.pas

@@ -8,7 +8,7 @@ interface
 
 uses
   fpcunit, testutils, testregistry, testdecorator,
-  Classes, SysUtils, db;
+  Classes, SysUtils, db, ToolsUnit;
 
 type
 
@@ -119,9 +119,12 @@ type
     procedure TestCanModifySpecialFields;
   end;
 
-  { TSQLTestSetup }
+  TTestUniDirectionalDBBasics = class(TTestDBBasics)
+  end;
+
+  { TDBBasicsUniDirectionalTestSetup }
 
-  TDBBasicsTestSetup = class(TTestSetup)
+  TDBBasicsUniDirectionalTestSetup = class(TDBBasicsTestSetup)
   protected
     procedure OneTimeSetup; override;
     procedure OneTimeTearDown; override;
@@ -129,7 +132,7 @@ type
 
 implementation
 
-uses toolsunit, bufdataset, variants, strutils;
+uses bufdataset, variants, strutils, sqldb;
 
 type THackDataLink=class(TdataLink);
 
@@ -232,38 +235,43 @@ var i,count      : integer;
 begin
   aDatasource := TDataSource.Create(nil);
   aDatalink := TTestDataLink.Create;
-  aDatalink.DataSource := aDatasource;
-  ABufferCount := 11;
-  aDatalink.BufferCount := ABufferCount;
-  DataEvents := '';
-  for count := 0 to 32 do
-    begin
-    aDatasource.DataSet := DBConnector.GetNDataset(count);
-    with aDatasource.Dataset do
+  try
+    aDatalink.DataSource := aDatasource;
+    ABufferCount := 11;
+    aDatalink.BufferCount := ABufferCount;
+    DataEvents := '';
+    for count := 0 to 32 do
       begin
-      i := 1;
-      Open;
-      AssertEquals('deUpdateState:0;',DataEvents);
-      DataEvents := '';
-      while not EOF do
+      aDatasource.DataSet := DBConnector.GetNDataset(count);
+      with aDatasource.Dataset do
         begin
-        AssertEquals(i,fields[0].AsInteger);
-        AssertEquals('TestName'+inttostr(i),fields[1].AsString);
-        inc(i);
-
-        Next;
-        if (i > ABufferCount) and not EOF then
-          AssertEquals('deCheckBrowseMode:0;deDataSetScroll:-1;DataSetScrolled:1;',DataEvents)
-        else
-          AssertEquals('deCheckBrowseMode:0;deDataSetScroll:0;DataSetScrolled:0;',DataEvents);
+        i := 1;
+        Open;
+        AssertEquals('deUpdateState:0;',DataEvents);
+        DataEvents := '';
+        while not EOF do
+          begin
+          AssertEquals(i,fields[0].AsInteger);
+          AssertEquals('TestName'+inttostr(i),fields[1].AsString);
+          inc(i);
+
+          Next;
+          if (i > ABufferCount) and not EOF then
+            AssertEquals('deCheckBrowseMode:0;deDataSetScroll:-1;DataSetScrolled:1;',DataEvents)
+          else
+            AssertEquals('deCheckBrowseMode:0;deDataSetScroll:0;DataSetScrolled:0;',DataEvents);
+          DataEvents := '';
+          end;
+        AssertEquals(count,i-1);
+        close;
+        AssertEquals('deUpdateState:0;',DataEvents);
         DataEvents := '';
         end;
-      AssertEquals(count,i-1);
-      close;
-      AssertEquals('deUpdateState:0;',DataEvents);
-      DataEvents := '';
       end;
-    end;
+  finally
+    aDatalink.Free;
+    aDatasource.Free;
+  end;
 end;
 
 procedure TTestDBBasics.TestdeFieldListChange;
@@ -326,23 +334,26 @@ var i,count     : integer;
 begin
   aDatasource := TDataSource.Create(nil);
   aDatalink := TTestDataLink.Create;
-  aDatalink.DataSource := aDatasource;
-  ds := DBConnector.GetNDataset(6);
-  ds.BeforeScroll := DBConnector.DataEvent;
-  with ds do
-    begin
-    aDatasource.DataSet := ds;
-    open;
-    DataEvents := '';
-    Resync([rmExact]);
-    AssertEquals('deDataSetChange:0;',DataEvents);
-    DataEvents := '';
-    next;
-    AssertEquals('deCheckBrowseMode:0;DataEvent;deDataSetScroll:0;DataSetScrolled:1;',DataEvents);
-    close;
-    end;
-  aDatasource.Free;
-  aDatalink.Free;
+  try
+    aDatalink.DataSource := aDatasource;
+    ds := DBConnector.GetNDataset(6);
+    ds.BeforeScroll := DBConnector.DataEvent;
+    with ds do
+      begin
+      aDatasource.DataSet := ds;
+      open;
+      DataEvents := '';
+      Resync([rmExact]);
+      AssertEquals('deDataSetChange:0;',DataEvents);
+      DataEvents := '';
+      next;
+      AssertEquals('deCheckBrowseMode:0;DataEvent;deDataSetScroll:0;DataSetScrolled:1;',DataEvents);
+      close;
+      end;
+  finally
+    aDatasource.Free;
+    aDatalink.Free;
+  end;
 end;
 
 procedure TTestDBBasics.TestLastAppendCancel;
@@ -579,21 +590,28 @@ begin
 end;
 
 procedure TTestDBBasics.TestFileNameProperty;
-var ds    : TDataset;
+var ds1,ds2: TDataset;
     LoadDs: TCustomBufDataset;
 begin
-  ds := DBConnector.GetNDataset(true,5);
-  if not (ds is TCustomBufDataset) then
-    Ignore('This test only applies to TCustomBufDataset and descendents.');
+  ds2 := nil;
+  ds1 := DBConnector.GetNDataset(true,5);
+  try
+    if not (ds1 is TCustomBufDataset) then
+      Ignore('This test only applies to TCustomBufDataset and descendents.');
 
-  ds.open;
-  TCustomBufDataset(ds).FileName:='test.xml';
-  ds.close;
+    ds1.open;
+    TCustomBufDataset(ds1).FileName:='test.xml';
+    ds1.close;
 
-  ds := DBConnector.GetNDataset(True,7);
-  TCustomBufDataset(ds).FileName:='test.xml';
-  ds.Open;
-  FTestXMLDatasetDefinition(Ds);
+    ds2 := DBConnector.GetNDataset(True,7);
+    TCustomBufDataset(ds2).FileName:='test.xml';
+    ds2.Open;
+    FTestXMLDatasetDefinition(Ds2);
+  finally
+    TCustomBufDataset(ds1).FileName:='';
+    if assigned(ds2) then
+      TCustomBufDataset(ds2).FileName:='';
+  end;
 end;
 
 procedure TTestDBBasics.TestClientDatasetAsMemDataset;
@@ -2169,20 +2187,25 @@ begin
     cancel;
     AssertTrue('Field isn''t NULL after cancel',fieldbyname('id').IsNull);
     end;
-
 end;
 
-{ TSQLTestSetup }
-procedure TDBBasicsTestSetup.OneTimeSetup;
+{ TDBBasicsUniDirectionalTestSetup }
+
+procedure TDBBasicsUniDirectionalTestSetup.OneTimeSetup;
 begin
-  InitialiseDBConnector;
+  inherited OneTimeSetup;
+  DBConnector.TestUniDirectional:=true;
 end;
 
-procedure TDBBasicsTestSetup.OneTimeTearDown;
+procedure TDBBasicsUniDirectionalTestSetup.OneTimeTearDown;
 begin
-  FreeAndNil(DBConnector);
+  DBConnector.TestUniDirectional:=false;
+  inherited OneTimeTearDown;
 end;
 
 initialization
   RegisterTestDecorator(TDBBasicsTestSetup, TTestDBBasics);
+
+  if uppercase(dbconnectorname)='SQL' then
+    RegisterTestDecorator(TDBBasicsUniDirectionalTestSetup, TTestUniDirectionalDBBasics);
 end.

+ 17 - 1
packages/fcl-db/tests/testfieldtypes.pas

@@ -87,6 +87,7 @@ type
     procedure TestAggregates;
 
     procedure TestStringLargerThen8192;
+    procedure TestQueryAfterReconnect; // bug 16438
 
     // SchemaType tests
     procedure TestTableNames;
@@ -920,7 +921,7 @@ procedure TTestFieldTypes.TearDown;
 begin
   if assigned(DBConnector) then
     TSQLDBConnector(DBConnector).Transaction.Rollback;
-  FreeAndNil(DBConnector);
+  FreeDBConnector;
 end;
 
 procedure TTestFieldTypes.RunTest;
@@ -929,6 +930,21 @@ begin
     inherited RunTest;
 end;
 
+procedure TTestFieldTypes.TestQueryAfterReconnect;
+var DS: TDataset;
+begin
+  ds := DBConnector.GetNDataset(true,5);
+  with ds do
+    begin
+    open;
+    close;
+    TSQLDBConnector(DBConnector).Connection.Close;
+    TSQLDBConnector(DBConnector).Connection.Open;
+    open;
+    close;
+    end;
+end;
+
 procedure TTestFieldTypes.TestLocateNull;
 var DS: TCustomBufDataset;
 begin

+ 46 - 1
packages/fcl-db/tests/toolsunit.pas

@@ -7,7 +7,7 @@ unit ToolsUnit;
 interface
 
 uses
-  Classes, SysUtils, DB;
+  Classes, SysUtils, DB, testdecorator;
   
 Const MaxDataSet = 35;
   
@@ -21,6 +21,8 @@ type
        FUsedDatasets : TFPList;
        FChangedFieldDataset : boolean;
      protected
+       procedure SetTestUniDirectional(const AValue: boolean); virtual;
+       function GetTestUniDirectional: boolean; virtual;
        // These methods should be implemented by any descendents
        // They are called eacht time a test need a TDataset descendent
        Function InternalGetNDataset(n : integer) : TDataset;  virtual; abstract;
@@ -57,8 +59,16 @@ type
 
        procedure StartTest;
        procedure StopTest;
+       property TestUniDirectional: boolean read GetTestUniDirectional write SetTestUniDirectional;
      end;
 
+  { TDBBasicsTestSetup }
+
+  TDBBasicsTestSetup = class(TTestSetup)
+    protected
+      procedure OneTimeSetup; override;
+      procedure OneTimeTearDown; override;
+    end;
 
 { TTestDataLink }
 
@@ -158,12 +168,15 @@ var dbtype,
 
 
 procedure InitialiseDBConnector;
+procedure FreeDBConnector;
 
 implementation
 
 uses
   inifiles;
 
+var DBConnectorRefCount: integer;
+
 constructor TDBConnector.create;
 begin
   CreateFieldDataset;
@@ -178,6 +191,16 @@ begin
   DropFieldDataset;
 end;
 
+function TDBConnector.GetTestUniDirectional: boolean;
+begin
+  result := false;
+end;
+
+procedure TDBConnector.SetTestUniDirectional(const AValue: boolean);
+begin
+  raise exception.create('Connector does not support tests for unidirectional datasets');
+end;
+
 procedure TDBConnector.ResetNDatasets;
 begin
   DropNDatasets;
@@ -226,6 +249,7 @@ procedure InitialiseDBConnector;
 var DBConnectorClass : TPersistentClass;
     i                : integer;
 begin
+  if DBConnectorRefCount>0 then exit;
   testValues[ftString] := testStringValues;
   testValues[ftFixedChar] := testStringValues;
   for i := 0 to testValuesCount-1 do
@@ -249,6 +273,14 @@ begin
   if assigned(DBConnectorClass) then
     DBConnector := TDBConnectorClass(DBConnectorClass).create
   else Raise Exception.Create('Unknown db-connector specified');
+  inc(DBConnectorRefCount);
+end;
+
+procedure FreeDBConnector;
+begin
+  dec(DBConnectorRefCount);
+  if DBConnectorRefCount=0 then
+    FreeAndNil(DBConnector);
 end;
 
 { TTestDataLink }
@@ -324,7 +356,20 @@ begin
     end;
 end;
 
+{ TDBBasicsTestSetup }
+
+procedure TDBBasicsTestSetup.OneTimeSetup;
+begin
+  InitialiseDBConnector;
+end;
+
+procedure TDBBasicsTestSetup.OneTimeTearDown;
+begin
+  FreeDBConnector;
+end;
+
 initialization
   ReadIniFile;
+  DBConnectorRefCount:=0;
 end.
 

+ 164 - 122
packages/fcl-web/Makefile

@@ -2,9 +2,9 @@
 # Don't edit, this file is generated by FPCMake Version 2.0.0 [2010/11/19]
 #
 default: all
-MAKEFILETARGETS=i386-linux i386-go32v2 i386-win32 i386-os2 i386-freebsd i386-beos i386-haiku i386-netbsd i386-solaris i386-qnx i386-netware i386-openbsd i386-wdosx i386-darwin i386-emx i386-watcom i386-netwlibc i386-wince i386-embedded i386-symbian m68k-linux m68k-freebsd m68k-netbsd m68k-amiga m68k-atari m68k-openbsd m68k-palmos m68k-embedded powerpc-linux powerpc-netbsd powerpc-amiga powerpc-macos powerpc-darwin powerpc-morphos powerpc-embedded sparc-linux sparc-netbsd sparc-solaris sparc-embedded x86_64-linux x86_64-freebsd x86_64-solaris x86_64-darwin x86_64-win64 x86_64-embedded arm-linux arm-palmos arm-darwin arm-wince arm-gba arm-nds arm-embedded arm-symbian powerpc64-linux powerpc64-darwin powerpc64-embedded avr-embedded armeb-linux armeb-embedded mipsel-linux
+MAKEFILETARGETS=i386-linux i386-go32v2 i386-win32 i386-os2 i386-freebsd i386-beos i386-haiku i386-netbsd i386-solaris i386-qnx i386-netware i386-openbsd i386-wdosx i386-darwin i386-emx i386-watcom i386-netwlibc i386-wince i386-embedded i386-symbian m68k-linux m68k-freebsd m68k-netbsd m68k-amiga m68k-atari m68k-openbsd m68k-palmos m68k-embedded powerpc-linux powerpc-netbsd powerpc-amiga powerpc-macos powerpc-darwin powerpc-morphos powerpc-embedded sparc-linux sparc-netbsd sparc-solaris sparc-embedded x86_64-linux x86_64-freebsd x86_64-darwin x86_64-win64 x86_64-embedded arm-linux arm-palmos arm-darwin arm-wince arm-gba arm-nds arm-embedded arm-symbian powerpc64-linux powerpc64-darwin powerpc64-embedded avr-embedded armeb-linux armeb-embedded
 BSDs = freebsd netbsd openbsd darwin
-UNIXs = linux $(BSDs) solaris qnx haiku
+UNIXs = linux $(BSDs) solaris qnx
 LIMIT83fs = go32v2 os2 emx watcom
 OSNeedsComspecToRunBatch = go32v2 watcom
 FORCE:
@@ -59,11 +59,9 @@ endif
 endif
 ifdef COMSPEC
 ifneq ($(findstring $(OS_SOURCE),$(OSNeedsComspecToRunBatch)),)
-ifndef RUNBATCH
 RUNBATCH=$(COMSPEC) /C
 endif
 endif
-endif
 ifdef inUnix
 PATHSEP=/
 else
@@ -267,184 +265,178 @@ PACKAGESDIR:=$(wildcard $(FPCDIR) $(FPCDIR)/packages $(FPCDIR)/packages/base $(F
 override PACKAGE_NAME=fcl-web
 override PACKAGE_VERSION=2.4.3
 ifeq ($(FULL_TARGET),i386-linux)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),i386-go32v2)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),i386-win32)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),i386-os2)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),i386-freebsd)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),i386-beos)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),i386-haiku)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),i386-netbsd)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),i386-solaris)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),i386-qnx)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),i386-netware)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),i386-openbsd)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),i386-wdosx)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),i386-darwin)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),i386-emx)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),i386-watcom)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),i386-netwlibc)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),i386-wince)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),i386-embedded)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),i386-symbian)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),m68k-linux)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),m68k-freebsd)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),m68k-netbsd)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),m68k-amiga)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),m68k-atari)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),m68k-openbsd)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),m68k-palmos)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),m68k-embedded)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),powerpc-linux)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),powerpc-netbsd)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),powerpc-amiga)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),powerpc-macos)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),powerpc-darwin)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),powerpc-morphos)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),powerpc-embedded)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),sparc-linux)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),sparc-netbsd)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),sparc-solaris)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),sparc-embedded)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),x86_64-linux)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),x86_64-freebsd)
-override TARGET_DIRS+=src/base src/webdata
-endif
-ifeq ($(FULL_TARGET),x86_64-solaris)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),x86_64-darwin)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),x86_64-win64)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),x86_64-embedded)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),arm-linux)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),arm-palmos)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),arm-darwin)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),arm-wince)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),arm-gba)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),arm-nds)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),arm-embedded)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),arm-symbian)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),powerpc64-linux)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),powerpc64-darwin)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),powerpc64-embedded)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),avr-embedded)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),armeb-linux)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 ifeq ($(FULL_TARGET),armeb-embedded)
-override TARGET_DIRS+=src/base src/webdata
-endif
-ifeq ($(FULL_TARGET),mipsel-linux)
-override TARGET_DIRS+=src/base src/webdata
+override TARGET_DIRS+=src/base src/webdata src/jsonrpc
 endif
 override INSTALL_FPCPACKAGE=y
 ifeq ($(FULL_TARGET),i386-linux)
@@ -570,9 +562,6 @@ endif
 ifeq ($(FULL_TARGET),x86_64-freebsd)
 override COMPILER_OPTIONS+=-S2h
 endif
-ifeq ($(FULL_TARGET),x86_64-solaris)
-override COMPILER_OPTIONS+=-S2h
-endif
 ifeq ($(FULL_TARGET),x86_64-darwin)
 override COMPILER_OPTIONS+=-S2h
 endif
@@ -624,9 +613,6 @@ endif
 ifeq ($(FULL_TARGET),armeb-embedded)
 override COMPILER_OPTIONS+=-S2h
 endif
-ifeq ($(FULL_TARGET),mipsel-linux)
-override COMPILER_OPTIONS+=-S2h
-endif
 ifdef REQUIRE_UNITSDIR
 override UNITSDIR+=$(REQUIRE_UNITSDIR)
 endif
@@ -2065,19 +2051,6 @@ REQUIRE_PACKAGES_FCL-PROCESS=1
 REQUIRE_PACKAGES_HTTPD22=1
 REQUIRE_PACKAGES_FASTCGI=1
 endif
-ifeq ($(FULL_TARGET),x86_64-solaris)
-REQUIRE_PACKAGES_FCL-BASE=1
-REQUIRE_PACKAGES_FCL-XML=1
-REQUIRE_PACKAGES_FCL-DB=1
-REQUIRE_PACKAGES_FCL-JSON=1
-REQUIRE_PACKAGES_FCL-PASSRC=1
-REQUIRE_PACKAGES_FCL-ASYNC=1
-REQUIRE_PACKAGES_FCL-NET=1
-REQUIRE_PACKAGES_SQLITE=1
-REQUIRE_PACKAGES_FCL-PROCESS=1
-REQUIRE_PACKAGES_HTTPD22=1
-REQUIRE_PACKAGES_FASTCGI=1
-endif
 ifeq ($(FULL_TARGET),x86_64-darwin)
 REQUIRE_PACKAGES_UNIVINT=1
 REQUIRE_PACKAGES_FCL-BASE=1
@@ -2340,26 +2313,6 @@ REQUIRE_PACKAGES_FCL-PROCESS=1
 REQUIRE_PACKAGES_HTTPD22=1
 REQUIRE_PACKAGES_FASTCGI=1
 endif
-ifeq ($(FULL_TARGET),mipsel-linux)
-REQUIRE_PACKAGES_FCL-BASE=1
-REQUIRE_PACKAGES_ICONVENC=1
-REQUIRE_PACKAGES_FCL-XML=1
-REQUIRE_PACKAGES_FCL-DB=1
-REQUIRE_PACKAGES_FCL-JSON=1
-REQUIRE_PACKAGES_FCL-PASSRC=1
-REQUIRE_PACKAGES_FCL-ASYNC=1
-REQUIRE_PACKAGES_FCL-NET=1
-REQUIRE_PACKAGES_IBASE=1
-REQUIRE_PACKAGES_POSTGRES=1
-REQUIRE_PACKAGES_MYSQL=1
-REQUIRE_PACKAGES_ODBC=1
-REQUIRE_PACKAGES_ORACLE=1
-REQUIRE_PACKAGES_SQLITE=1
-REQUIRE_PACKAGES_PXLIB=1
-REQUIRE_PACKAGES_FCL-PROCESS=1
-REQUIRE_PACKAGES_HTTPD22=1
-REQUIRE_PACKAGES_FASTCGI=1
-endif
 ifdef REQUIRE_PACKAGES_FCL-BASE
 PACKAGEDIR_FCL-BASE:=$(firstword $(subst /Makefile.fpc,,$(strip $(wildcard $(addsuffix /fcl-base/Makefile.fpc,$(PACKAGESDIR))))))
 ifneq ($(PACKAGEDIR_FCL-BASE),)
@@ -3132,7 +3085,7 @@ ZIPCMD_CDPACK:=cd $(subst /,$(ZIPPATHSEP),$(PACKDIR))
 ZIPCMD_CDBASE:=cd $(subst /,$(ZIPPATHSEP),$(BASEDIR))
 ifdef USETAR
 ZIPDESTFILE:=$(DIST_DESTDIR)/$(FULLZIPNAME)$(TAREXT)
-ZIPCMD_ZIP:=$(TARPROG) c$(TAROPT)f $(ZIPDESTFILE) *
+ZIPCMD_ZIP:=$(TARPROG) cf$(TAROPT) $(ZIPDESTFILE) *
 else
 ZIPDESTFILE:=$(DIST_DESTDIR)/$(FULLZIPNAME)$(ZIPEXT)
 ZIPCMD_ZIP:=$(subst /,$(ZIPPATHSEP),$(ZIPPROG)) -Dr $(ZIPOPT) $(ZIPDESTFILE) *
@@ -3177,9 +3130,6 @@ fpc_zipdistinstall:
 ifdef EXEFILES
 override CLEANEXEFILES:=$(addprefix $(TARGETDIRPREFIX),$(CLEANEXEFILES))
 endif
-ifdef CLEAN_PROGRAMS
-override CLEANEXEFILES+=$(addprefix $(TARGETDIRPREFIX),$(addsuffix $(EXEEXT), $(CLEAN_PROGRAMS)))
-endif
 ifdef CLEAN_UNITS
 override CLEANPPUFILES+=$(addsuffix $(PPUEXT),$(CLEAN_UNITS))
 endif
@@ -3226,9 +3176,6 @@ endif
 ifdef CLEANRSTFILES
 	-$(DEL) $(addprefix $(UNITTARGETDIRPREFIX),$(CLEANRSTFILES))
 endif
-endif
-ifdef CLEAN_FILES
-	-$(DEL) $(CLEAN_FILES)
 endif
 	-$(DELTREE) units
 	-$(DEL) *$(OEXT) *$(PPUEXT) *$(RSTEXT) *$(ASMEXT) *$(STATICLIBEXT) *$(SHAREDLIBEXT) *$(PPLEXT)
@@ -3355,242 +3302,292 @@ fpc_makefiles: fpc_makefile fpc_makefile_dirs
 ifeq ($(FULL_TARGET),i386-linux)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),i386-go32v2)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),i386-win32)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),i386-os2)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),i386-freebsd)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),i386-beos)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),i386-haiku)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),i386-netbsd)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),i386-solaris)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),i386-qnx)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),i386-netware)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),i386-openbsd)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),i386-wdosx)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),i386-darwin)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),i386-emx)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),i386-watcom)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),i386-netwlibc)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),i386-wince)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),i386-embedded)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),i386-symbian)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),m68k-linux)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),m68k-freebsd)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),m68k-netbsd)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),m68k-amiga)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),m68k-atari)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),m68k-openbsd)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),m68k-palmos)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),m68k-embedded)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),powerpc-linux)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),powerpc-netbsd)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),powerpc-amiga)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),powerpc-macos)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),powerpc-darwin)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),powerpc-morphos)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),powerpc-embedded)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),sparc-linux)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),sparc-netbsd)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),sparc-solaris)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),sparc-embedded)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),x86_64-linux)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),x86_64-freebsd)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
-endif
-ifeq ($(FULL_TARGET),x86_64-solaris)
-TARGET_DIRS_SRC/BASE=1
-TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),x86_64-darwin)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),x86_64-win64)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),x86_64-embedded)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),arm-linux)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),arm-palmos)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),arm-darwin)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),arm-wince)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),arm-gba)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),arm-nds)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),arm-embedded)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),arm-symbian)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),powerpc64-linux)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),powerpc64-darwin)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),powerpc64-embedded)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),avr-embedded)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),armeb-linux)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifeq ($(FULL_TARGET),armeb-embedded)
 TARGET_DIRS_SRC/BASE=1
 TARGET_DIRS_SRC/WEBDATA=1
-endif
-ifeq ($(FULL_TARGET),mipsel-linux)
-TARGET_DIRS_SRC/BASE=1
-TARGET_DIRS_SRC/WEBDATA=1
+TARGET_DIRS_SRC/JSONRPC=1
 endif
 ifdef TARGET_DIRS_SRC/BASE
 src/base_all:
@@ -3682,6 +3679,51 @@ src/webdata:
 	$(MAKE) -C src/webdata all
 .PHONY: src/webdata_all src/webdata_debug src/webdata_smart src/webdata_release src/webdata_units src/webdata_examples src/webdata_shared src/webdata_install src/webdata_sourceinstall src/webdata_exampleinstall src/webdata_distinstall src/webdata_zipinstall src/webdata_zipsourceinstall src/webdata_zipexampleinstall src/webdata_zipdistinstall src/webdata_clean src/webdata_distclean src/webdata_cleanall src/webdata_info src/webdata_makefiles src/webdata
 endif
+ifdef TARGET_DIRS_SRC/JSONRPC
+src/jsonrpc_all:
+	$(MAKE) -C src/jsonrpc all
+src/jsonrpc_debug:
+	$(MAKE) -C src/jsonrpc debug
+src/jsonrpc_smart:
+	$(MAKE) -C src/jsonrpc smart
+src/jsonrpc_release:
+	$(MAKE) -C src/jsonrpc release
+src/jsonrpc_units:
+	$(MAKE) -C src/jsonrpc units
+src/jsonrpc_examples:
+	$(MAKE) -C src/jsonrpc examples
+src/jsonrpc_shared:
+	$(MAKE) -C src/jsonrpc shared
+src/jsonrpc_install:
+	$(MAKE) -C src/jsonrpc install
+src/jsonrpc_sourceinstall:
+	$(MAKE) -C src/jsonrpc sourceinstall
+src/jsonrpc_exampleinstall:
+	$(MAKE) -C src/jsonrpc exampleinstall
+src/jsonrpc_distinstall:
+	$(MAKE) -C src/jsonrpc distinstall
+src/jsonrpc_zipinstall:
+	$(MAKE) -C src/jsonrpc zipinstall
+src/jsonrpc_zipsourceinstall:
+	$(MAKE) -C src/jsonrpc zipsourceinstall
+src/jsonrpc_zipexampleinstall:
+	$(MAKE) -C src/jsonrpc zipexampleinstall
+src/jsonrpc_zipdistinstall:
+	$(MAKE) -C src/jsonrpc zipdistinstall
+src/jsonrpc_clean:
+	$(MAKE) -C src/jsonrpc clean
+src/jsonrpc_distclean:
+	$(MAKE) -C src/jsonrpc distclean
+src/jsonrpc_cleanall:
+	$(MAKE) -C src/jsonrpc cleanall
+src/jsonrpc_info:
+	$(MAKE) -C src/jsonrpc info
+src/jsonrpc_makefiles:
+	$(MAKE) -C src/jsonrpc makefiles
+src/jsonrpc:
+	$(MAKE) -C src/jsonrpc all
+.PHONY: src/jsonrpc_all src/jsonrpc_debug src/jsonrpc_smart src/jsonrpc_release src/jsonrpc_units src/jsonrpc_examples src/jsonrpc_shared src/jsonrpc_install src/jsonrpc_sourceinstall src/jsonrpc_exampleinstall src/jsonrpc_distinstall src/jsonrpc_zipinstall src/jsonrpc_zipsourceinstall src/jsonrpc_zipexampleinstall src/jsonrpc_zipdistinstall src/jsonrpc_clean src/jsonrpc_distclean src/jsonrpc_cleanall src/jsonrpc_info src/jsonrpc_makefiles src/jsonrpc
+endif
 all: $(addsuffix _all,$(TARGET_DIRS))
 debug: $(addsuffix _debug,$(TARGET_DIRS))
 smart: $(addsuffix _smart,$(TARGET_DIRS))

+ 1 - 1
packages/fcl-web/Makefile.fpc

@@ -7,7 +7,7 @@ name=fcl-web
 version=2.4.3
 
 [target]
-dirs=src/base src/webdata
+dirs=src/base src/webdata src/jsonrpc
 
 [require]
 packages=fcl-base fcl-xml fcl-db fcl-json fcl-net

+ 21 - 0
packages/fcl-web/examples/jsonrpc/demo1/README.txt

@@ -0,0 +1,21 @@
+
+This is an example of how to use JSON-RPC.
+
+It requires lazarus to compile.
+
+The various *.in files are input for JSON-RPC requests.
+
+The application can be tested as follows from the command-line
+
+testcgiapp -i demo -p echo/manual < echo.in
+testcgiapp -i demo -p echo/dispatch < echobatch.in
+testcgiapp -i demo -p echo/registered < echobatch.in
+testcgiapp -i demo -p echo/extdirect < extdirect.in
+testcgiapp -i demo -p echo/dispatch <notification.in
+testcgiapp -i demo -p echo/extdirectapi
+testcgiapp -i demo -p echo/content <echobatch.in
+testcgiapp -i demo -p echo/module <echobatch.in    
+
+The response is printed on standard output.
+
+the testcgiapp application is located in fcl-web/tests

+ 93 - 0
packages/fcl-web/examples/jsonrpc/demo1/demo.lpi

@@ -0,0 +1,93 @@
+<?xml version="1.0"?>
+<CONFIG>
+  <ProjectOptions>
+    <Version Value="7"/>
+    <General>
+      <Flags>
+        <MainUnitHasCreateFormStatements Value="False"/>
+        <Runnable Value="False"/>
+      </Flags>
+      <SessionStorage Value="InProjectDir"/>
+      <MainUnit Value="0"/>
+      <TargetFileExt Value=""/>
+      <Title Value="FPC JSON-RPC demo "/>
+      <ResourceType Value="res"/>
+      <UseXPManifest Value="True"/>
+      <Icon Value="0"/>
+    </General>
+    <VersionInfo>
+      <Language Value=""/>
+      <CharSet Value=""/>
+      <StringTable Comments="" CompanyName="" FileDescription="" FileVersion="" InternalName="" LegalCopyright="" LegalTrademarks="" OriginalFilename="" ProductName="" ProductVersion=""/>
+    </VersionInfo>
+    <PublishOptions>
+      <Version Value="2"/>
+      <IgnoreBinaries Value="False"/>
+      <IncludeFileFilter Value="*.(pas|pp|inc|lfm|lpr|lrs|lpi|lpk|sh|xml)"/>
+      <ExcludeFileFilter Value="*.(bak|ppu|ppw|o|so);*~;backup"/>
+    </PublishOptions>
+    <RunParams>
+      <local>
+        <FormatVersion Value="1"/>
+        <LaunchingApplication PathPlusParams="/usr/X11R6/bin/xterm -T 'Lazarus Run Output' -e $(LazarusDir)/tools/runwait.sh $(TargetCmdLine)"/>
+      </local>
+    </RunParams>
+    <RequiredPackages Count="3">
+      <Item1>
+        <PackageName Value="WebLaz"/>
+      </Item1>
+      <Item2>
+        <PackageName Value="LCL"/>
+      </Item2>
+      <Item3>
+        <PackageName Value="FCL"/>
+      </Item3>
+    </RequiredPackages>
+    <Units Count="2">
+      <Unit0>
+        <Filename Value="demo.lpr"/>
+        <IsPartOfProject Value="True"/>
+        <UnitName Value="demo"/>
+      </Unit0>
+      <Unit1>
+        <Filename Value="wmdemo.pp"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="FPWebModule1"/>
+        <ResourceBaseClass Value="DataModule"/>
+        <UnitName Value="wmdemo"/>
+      </Unit1>
+    </Units>
+  </ProjectOptions>
+  <CompilerOptions>
+    <Version Value="8"/>
+    <SearchPaths>
+      <IncludeFiles Value="$(ProjOutDir)/"/>
+    </SearchPaths>
+    <Parsing>
+      <SyntaxOptions>
+        <UseAnsiStrings Value="True"/>
+      </SyntaxOptions>
+    </Parsing>
+    <Linking>
+      <Debugging>
+        <UseHeaptrc Value="True"/>
+      </Debugging>
+    </Linking>
+    <Other>
+      <CompilerPath Value="$(CompPath)"/>
+    </Other>
+  </CompilerOptions>
+  <Debugging>
+    <Exceptions Count="3">
+      <Item1>
+        <Name Value="EAbort"/>
+      </Item1>
+      <Item2>
+        <Name Value="ECodetoolError"/>
+      </Item2>
+      <Item3>
+        <Name Value="EFOpenError"/>
+      </Item3>
+    </Exceptions>
+  </Debugging>
+</CONFIG>

+ 16 - 0
packages/fcl-web/examples/jsonrpc/demo1/demo.lpr

@@ -0,0 +1,16 @@
+program demo;
+
+{$mode objfpc}{$H+}
+
+uses
+  fpCGI, wmdemo;
+
+// {$R *.res}
+
+
+begin
+  Application.Title:='FPC JSON-RPC demo';
+  Application.Initialize;
+  Application.Run;
+end.
+

+ 1 - 0
packages/fcl-web/examples/jsonrpc/demo1/echo.in

@@ -0,0 +1 @@
+{ "method": "echo", "params": ["Hello JSON-RPC"], "id": 1 }

+ 4 - 0
packages/fcl-web/examples/jsonrpc/demo1/echobatch.in

@@ -0,0 +1,4 @@
+[
+  { "method": "echo", "params": ["Hello JSON-RPC 1"], "id": 1 },
+  { "method": "echo", "params": ["Hello JSON-RPC 2"], "id": 2 }
+]

+ 4 - 0
packages/fcl-web/examples/jsonrpc/demo1/extdirect.in

@@ -0,0 +1,4 @@
+[
+  { "action": "test", "method": "echo", "data": ["Hello JSON-RPC 1"], "tid": 1 },
+  { "action": "test", "method": "echo", "data": ["Hello JSON-RPC 2"], "tid": 2 }
+]

+ 1 - 0
packages/fcl-web/examples/jsonrpc/demo1/notification.in

@@ -0,0 +1 @@
+{ "method": "echo", "params": ["Hello JSON-RPC"], "id": null }

+ 46 - 0
packages/fcl-web/examples/jsonrpc/demo1/wmdemo.lfm

@@ -0,0 +1,46 @@
+object FPWebModule1: TFPWebModule1
+  OnCreate = DataModuleCreate
+  OldCreateOrder = False
+  Actions = <  
+    item
+      Name = 'Manual'
+      Default = True
+      OnRequest = TFPWebActions0Request
+    end  
+    item
+      Name = 'Dispatch'
+      Default = False
+      OnRequest = TFPWebActions1Request
+    end  
+    item
+      Name = 'Registered'
+      Default = False
+      OnRequest = TFPWebActions2Request
+    end  
+    item
+      Name = 'ExtDirect'
+      Default = False
+      OnRequest = TFPWebActions3Request
+    end  
+    item
+      Name = 'Content'
+      Default = False
+      OnRequest = TFPWebActions4Request
+    end  
+    item
+      Name = 'ExtDirectAPI'
+      Default = False
+      OnRequest = TFPWebActions5Request
+    end  
+    item
+      Name = 'Module'
+      Default = False
+      OnRequest = TFPWebActions6Request
+    end>
+  ActionVar = 'Action'
+  CreateSession = False
+  Height = 260
+  HorizontalOffset = 578
+  VerticalOffset = 373
+  Width = 442
+end

+ 15 - 0
packages/fcl-web/examples/jsonrpc/demo1/wmdemo.lrs

@@ -0,0 +1,15 @@
+{ This is an automatically generated lazarus resource file }
+
+LazarusResources.Add('TFPWebModule1','FORMDATA',[
+  'TPF0'#13'TFPWebModule1'#12'FPWebModule1'#8'OnCreate'#7#16'DataModuleCreate'
+  +#14'OldCreateOrder'#8#7'Actions'#14#1#4'Name'#6#6'Manual'#7'Default'#9#9'OnR'
+  +'equest'#7#21'TFPWebActions0Request'#0#1#4'Name'#6#8'Dispatch'#7'Default'#8#9
+  +'OnRequest'#7#21'TFPWebActions1Request'#0#1#4'Name'#6#10'Registered'#7'Defau'
+  +'lt'#8#9'OnRequest'#7#21'TFPWebActions2Request'#0#1#4'Name'#6#9'ExtDirect'#7
+  +'Default'#8#9'OnRequest'#7#21'TFPWebActions3Request'#0#1#4'Name'#6#7'Content'
+  +#7'Default'#8#9'OnRequest'#7#21'TFPWebActions4Request'#0#1#4'Name'#6#12'ExtD'
+  +'irectAPI'#7'Default'#8#9'OnRequest'#7#21'TFPWebActions5Request'#0#1#4'Name'
+  +#6#6'Module'#7'Default'#8#9'OnRequest'#7#21'TFPWebActions6Request'#0#0#9'Act'
+  +'ionVar'#6#6'Action'#13'CreateSession'#8#6'Height'#3#4#1#16'HorizontalOffset'
+  +#3'B'#2#14'VerticalOffset'#3'u'#1#5'Width'#3#186#1#0#0
+]);

+ 383 - 0
packages/fcl-web/examples/jsonrpc/demo1/wmdemo.pp

@@ -0,0 +1,383 @@
+unit wmdemo; 
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+  Classes, SysUtils, FileUtil, LResources, HTTPDefs, websession, fpHTTP, fpWeb; 
+
+type
+
+  { TFPWebModule1 }
+
+  TFPWebModule1 = class(TFPWebModule)
+    procedure DataModuleCreate(Sender: TObject);
+    procedure TFPWebActions0Request(Sender: TObject; ARequest: TRequest;
+      AResponse: TResponse; var Handled: Boolean);
+    procedure TFPWebActions1Request(Sender: TObject; ARequest: TRequest;
+      AResponse: TResponse; var Handled: Boolean);
+    procedure TFPWebActions2Request(Sender: TObject; ARequest: TRequest;
+      AResponse: TResponse; var Handled: Boolean);
+    procedure TFPWebActions3Request(Sender: TObject; ARequest: TRequest;
+      AResponse: TResponse; var Handled: Boolean);
+    procedure TFPWebActions4Request(Sender: TObject; ARequest: TRequest;
+      AResponse: TResponse; var Handled: Boolean);
+    procedure TFPWebActions5Request(Sender: TObject; ARequest: TRequest;
+      AResponse: TResponse; var Handled: Boolean);
+    procedure TFPWebActions6Request(Sender: TObject; ARequest: TRequest;
+      AResponse: TResponse; var Handled: Boolean);
+  private
+    { private declarations }
+  public
+    { public declarations }
+  end; 
+
+var
+  FPWebModule1: TFPWebModule1; 
+
+implementation
+
+Uses fpjson,jsonparser,fpjsonrpc,webjsonrpc, fpextdirect;
+
+{ TFPWebModule1 }
+
+procedure TFPWebModule1.DataModuleCreate(Sender: TObject);
+begin
+end;
+
+procedure TFPWebModule1.TFPWebActions0Request(Sender: TObject;
+  ARequest: TRequest; AResponse: TResponse; var Handled: Boolean);
+{
+  Demo 1. Manually do everything.
+  Only a single method call is supported.
+}
+Var
+  P : TJSONParser;
+  Req,Res : TJSONData;
+  Env,O : TJSONObject;
+  M : TJSONStringType;
+  E : TJSONRPCEcho;
+  I : Integer;
+  ID : TJSONData;
+  Err : TJSONData;
+
+begin
+  Res:=Nil;
+  Err:=Nil;
+  ID:=Nil;
+  try
+    P:=TJSONParser.Create(ARequest.Content);
+    try
+      Req:=P.Parse;
+      try
+        If Not (Req is TJSONObject) then
+          JSONRPCError(SErrRequestMustBeObject);
+        O:=(Req as TJSONObject);
+        I:=O.IndexOfName('id');
+        If (I=-1) then
+          JSONRPCError(SErrNoIDProperty);
+        ID:=O.Items[i].Clone;
+        if O.IndexOfName('method')=-1 then
+          JSONRPCError(SErrNoMethodName);
+        M:=O.Strings['method'];
+        If (m<>'echo') then
+          JSONRPCError('Only echo method is supported');
+        E:=TJSONRPCEcho.Create(Self);
+        try
+          I:=O.IndexOfName('params');
+          Res:=E.Execute(O.Items[i],Nil);
+        finally
+          E.Free;
+        end;
+      finally
+        Req.Free;
+      end;
+    finally
+      P.Free;
+    end;
+  except
+    On E : Exception do
+      Err:=TJSONObject.Create(['message',E.Message,'name',E.ClassName,'code',-1]);
+  end;
+  If Assigned(ID) and (ID.JSONType<>jtNull) then
+    begin
+    Env:=TJSONObject.Create();
+    try
+      If not Assigned(Res) then
+        Res:=TJSONNull.Create;
+      Env.Add('result',Res);
+      If (Err=Nil) then
+        Err:=TJSONNull.Create;
+      Env.Add('error',Err);
+      Env.Add('id',ID);
+      AResponse.Content:=Env.AsJSON;
+    finally
+      Env.Free;
+    end;
+    end;
+  AResponse.SendContent;
+  Handled:=True;
+end;
+
+procedure TFPWebModule1.TFPWebActions1Request(Sender: TObject;
+  ARequest: TRequest; AResponse: TResponse; var Handled: Boolean);
+
+{
+  Demo 2. Use a dispatcher to dispatch the requests.
+  The handler is located on the owner module
+  (it is created run-time, though)
+}
+
+Var
+  Echohandler:TJSONRPCEcho;
+  Disp : TJSONRPCDispatcher;
+  P : TJSONParser;
+  Req,res : TJSONData;
+  O : TJSONRPCDispatchOptions;
+
+begin
+  Echohandler:=TJSONRPCEcho.Create(Self);
+  try
+    EchoHandler.Name:='echo';
+    Disp:=TJSONRPCDispatcher.Create(Self);
+    try
+      O:=Disp.Options;
+      Include(O,jdoRequireClass);
+      Disp.Options:=O;
+      P:= TJSONParser.Create(ARequest.Content);
+      try
+        Req:=P.Parse;
+        try
+          Res:=Nil;
+          Res:=Disp.Execute(Req,Nil);
+          try
+            If Assigned(Res) then
+              begin
+              AResponse.Content:=Res.AsJSON;
+              end;
+            AResponse.SendContent;
+            Handled:=True;
+          finally
+            FreeAndNil(Res);
+          end;
+        finally
+          Req.Free;
+        end;
+      finally
+        P.Free;
+      end;
+    finally
+      Disp.Free;
+    end;
+  finally
+    EchoHandler.Free;
+  end;
+
+end;
+
+procedure TFPWebModule1.TFPWebActions2Request(Sender: TObject;
+  ARequest: TRequest; AResponse: TResponse; var Handled: Boolean);
+{
+  Demo 3. Use a dispatcher to dispatch the requests,
+  The handler is registered in the JSONFPCHandlerManager.
+  (it is created run-time, though)
+}
+
+Var
+  Disp : TJSONRPCDispatcher;
+  P : TJSONParser;
+  Req,res : TJSONData;
+  O : TJSONRPCDispatchOptions;
+
+begin
+  JSONRpcHandlerManager.RegisterHandler('','echo',TJSONRPCEcho);
+  try
+    Disp:=TJSONRPCDispatcher.Create(Self);
+    try
+      O:=Disp.Options;
+      Include(O,jdoSearchRegistry);
+      Disp.Options:=O;
+      P:= TJSONParser.Create(ARequest.Content);
+      try
+        Req:=P.Parse;
+        try
+          Res:=Nil;
+          Res:=Disp.Execute(Req,Nil);
+          try
+            If Assigned(Res) then
+              begin
+              AResponse.Content:=Res.AsJSON;
+              end;
+            AResponse.SendContent;
+            Handled:=True;
+          finally
+            FreeAndNil(Res);
+          end;
+        finally
+          Req.Free;
+        end;
+      finally
+        P.Free;
+      end;
+    finally
+      Disp.Free;
+    end;
+  finally
+    JSONRpcHandlerManager.UnRegisterHandler('','echo');
+  end;
+end;
+
+procedure TFPWebModule1.TFPWebActions3Request(Sender: TObject;
+  ARequest: TRequest; AResponse: TResponse; var Handled: Boolean);
+
+{
+  Demo 4. Ext.Direct dispatcher
+  The handler is registered in the JSONFPCHandlerManager.
+  (it is created run-time, though)
+}
+
+Var
+  Disp : TExtDirectDispatcher;
+  P : TJSONParser;
+  Req,res : TJSONData;
+  O : TJSONRPCDispatchOptions;
+
+begin
+  JSONRpcHandlerManager.RegisterHandler('test','echo',TJSONRPCEcho);
+  try
+    Disp:=TExtDirectDispatcher.Create(Self);
+    try
+      O:=Disp.Options;
+      Include(O,jdoSearchRegistry);
+      Disp.Options:=O;
+      P:= TJSONParser.Create(ARequest.Content);
+      try
+        Req:=P.Parse;
+        try
+          Res:=Nil;
+          Res:=Disp.Execute(Req,Nil);
+          try
+            If Assigned(Res) then
+              begin
+              AResponse.Content:=Res.AsJSON;
+              end;
+            AResponse.ContentLength:=Length(AResponse.Content);
+            AResponse.SendContent;
+            Handled:=True;
+          finally
+            FreeAndNil(Res);
+          end;
+        finally
+          Req.Free;
+        end;
+      finally
+        P.Free;
+      end;
+    finally
+      Disp.Free;
+    end;
+  finally
+    JSONRpcHandlerManager.UnRegisterHandler('','echo');
+  end;
+end;
+
+procedure TFPWebModule1.TFPWebActions4Request(Sender: TObject;
+  ARequest: TRequest; AResponse: TResponse; var Handled: Boolean);
+
+{
+  Demo 5. Using a TJSONRPCContentProducer.
+  The handler is registered in the JSONFPCHandlerManager.
+  (it is created run-time, though)
+}
+
+Var
+  Cont : TJSONRPCContentProducer;
+  disp : TJSONRPCDispatcher;
+
+begin
+  JSONRpcHandlerManager.RegisterHandler('test','echo',TJSONRPCEcho);
+  try
+    Cont:=TJSONRPCContentProducer.Create(Self);
+    try
+      disp:=TJSONRPCDispatcher.Create(Cont);
+      Disp.Options:=Disp.OPtions+[jdoSearchRegistry];
+      Cont.Dispatcher:=Disp;
+      AResponse.ContentStream:=TMemoryStream.Create;
+      try
+        Cont.GetContent(ARequest,AResponse.ContentStream,Handled);
+        AResponse.ContentLength:=AResponse.ContentStream.Size;
+        If Handled then
+          AResponse.SendContent;
+      finally
+        AResponse.ContentStream.Free;
+      end;
+    finally
+      Cont.Free;
+    end;
+  finally
+    JSONRpcHandlerManager.UnRegisterHandler('','echo');
+  end;
+end;
+
+procedure TFPWebModule1.TFPWebActions5Request(Sender: TObject;
+  ARequest: TRequest; AResponse: TResponse; var Handled: Boolean);
+{
+  Demo 6. creating an API response for Ext.Direct
+  The handler is registered in the JSONFPCHandlerManager.
+  (it is created run-time, though)
+}
+
+Var
+  D : TExtDirectDispatcher;
+  I : Integer;
+
+begin
+  JSONRpcHandlerManager.RegisterHandler('test','echo',TJSONRPCEcho);
+  try
+    D:=TExtDirectDispatcher.Create(Self);
+    try
+      D.URL:=BaseURL+'ExtDirect';
+      D.Options:=D.Options+[jdoSearchRegistry];
+      AResponse.Content:=D.APIAsString;
+      AResponse.ContentLength:=Length(AResponse.Content);
+    finally
+      D.Free;
+    end;
+  finally
+    JSONRpcHandlerManager.UnRegisterHandler('','echo');
+  end;
+end;
+
+procedure TFPWebModule1.TFPWebActions6Request(Sender: TObject;
+  ARequest: TRequest; AResponse: TResponse; var Handled: Boolean);
+{
+  Demo 6. Using a TJSONRPCModule instance to handle the request.
+  The handler is registered in the JSONFPCHandlerManager.
+  (it is created run-time, though)
+}
+
+Var
+  M : TJSONRPCModule;
+
+begin
+  JSONRpcHandlerManager.RegisterHandler('test','echo',TJSONRPCEcho);
+  try
+    M:=TJSONRPCModule.CreateNew(Self,0);
+    try
+      M.HandleRequest(ARequest,AResponse);
+      Handled:=True;
+    finally
+      M.Free;
+    end;
+  finally
+    JSONRpcHandlerManager.UnRegisterHandler('','echo');
+  end;
+end;
+
+initialization
+  {$I wmdemo.lrs}
+
+  RegisterHTTPModule('echo', TFPWebModule1);
+end.
+

+ 14 - 1
packages/fcl-web/fpmake.pp

@@ -35,6 +35,7 @@ begin
 
     P.SourcePath.Add('src/base');
     P.SourcePath.Add('src/webdata');
+    P.SourcePath.Add('src/jsonrpc');
 
     T:=P.Targets.AddUnit('cgiapp.pp');
     T.ResourceStrings:=true;
@@ -152,13 +153,25 @@ begin
       AddUnit('httpdefs');
       AddUnit('fpextjs');
       end;
-    T:=P.Targets.AddUnit('extjsjson.pp');
+    T:=P.Targets.AddUnit('extjsjson.pp'); 
     With T.Dependencies do
       begin
       AddUnit('fpwebdata');
       AddUnit('httpdefs');
       AddUnit('fpextjs');
       end;
+    T:=P.Targets.AddUnit('fpjsonrpc.pp');
+    T:=P.Targets.AddUnit('webjsonrpc.pp');
+    With T.Dependencies do
+      begin
+      AddUnit('fpjsonrpc');
+      end;
+    T:=P.Targets.AddUnit('fpextdirect.pp');
+    With T.Dependencies do
+      begin
+      AddUnit('fpjsonrpc');
+      AddUnit('webjsonrpc');
+      end;
 {$ifndef ALLPACKAGES}
     Run;
     end;

+ 1 - 5
packages/fcl-web/src/base/custcgi.pp

@@ -388,11 +388,7 @@ begin
   Case Index of
     21,
     34 : Result:=DecodeVar(14); // Property ServerName and Host
-    25 : begin
-         Result:=Decodevar(5); // Property PathInfo
-         If (Result='') then
-           Result:=Decodevar(34); // Property Request URI
-         end;
+    25 : Result:=Decodevar(5); // Property PathInfo
     26 : Result:=DecodeVar(6); // Property PathTranslated
     27 : Result:=DecodeVar(8); // Property RemoteAddress
     28 : Result:=DecodeVar(9); // Property RemoteHost

+ 1 - 1
packages/fcl-web/src/base/custweb.pp

@@ -368,7 +368,7 @@ Var
 
 begin
   S:=IncludeHTTPPathDelimiter(GetApplicationURL(ARequest));
-  P:=IncludeHTTPPathDelimiter(ARequest.ProcessedPathinfo);
+  P:=IncludeHTTPPathDelimiter(ARequest.ReturnedPathInfo);
   If (P='') or (P='/') then
     P:=IncludeHTTPPathDelimiter(AModuleName);
   if (Length(P)>0) and (P[1]='/') then

+ 1 - 1
packages/fcl-web/src/base/fpapache.pp

@@ -502,7 +502,7 @@ Constructor TApacheRequest.CreateReq(App : TCustomApacheApplication; ARequest :
 begin
   FApache:=App;
   FRequest:=Arequest;
-  ProcessedPathInfo:=App.BaseLocation;
+  ReturnedPathInfo:=App.BaseLocation;
   Inherited Create;
   InitFromRequest;
 end;

+ 1 - 0
packages/fcl-web/src/base/fphttp.pp

@@ -258,6 +258,7 @@ begin
   M:=TMemoryStream.Create;
   DoGetContent(ARequest,M,Handled);
   AResponse.ContentStream:=M;
+  AResponse.ContentLength:=M.Size;
 end;
 
 procedure THTTPContentProducer.DoGetContent(ARequest: TRequest; Content: TStream; Var Handled : Boolean);

+ 7 - 7
packages/fcl-web/src/base/httpdefs.pp

@@ -267,7 +267,7 @@ type
     FHandleGetOnPost: Boolean;
     FURI: String;
     FFiles : TUploadedFiles;
-    FProcessedPathInfo : String;
+    FReturnedPathInfo : String;
     procedure ParseFirstHeaderLine(const line: String);override;
     function GetFirstHeaderLine: String;
   Protected
@@ -286,7 +286,7 @@ type
     constructor Create; override;
     destructor destroy; override;
     Function  GetNextPathInfo : String;
-    Property  ProcessedPathInfo : String Read FProcessedPathInfo Write FProcessedPathInfo;
+    Property  ReturnedPathInfo : String Read FReturnedPathInfo Write FReturnedPathInfo;
     Property  CommandLine : String Read FCommandLine;
     Property  Command : String read FCommand;
     Property  URI : String read FURI;                // Uniform Resource Identifier
@@ -934,19 +934,19 @@ Var
   
 begin
   P:=PathInfo;
-{$ifdef CGIDEBUG}SendDebug(Format('Pathinfo: "%s" "%s"',[P,FProcessedPathInfo]));{$ENDIF}
+{$ifdef CGIDEBUG}SendDebug(Format('Pathinfo: "%s" "%s"',[P,FReturnedPathInfo]));{$ENDIF}
   if (P <> '') and (P[length(P)] = '/') then
     Delete(P, length(P), 1);//last char is '/'
   If (P<>'') and (P[1]='/') then
     Delete(P,1,1);
-  Delete(P,1,Length(IncludeHTTPPathDelimiter(FProcessedPathInfo)));
- {$ifdef CGIDEBUG}SendDebug(Format('Pathinfo: "%s" "%s"',[P,FProcessedPathInfo]));{$ENDIF}
+  Delete(P,1,Length(IncludeHTTPPathDelimiter(FReturnedPathInfo)));
+ {$ifdef CGIDEBUG}SendDebug(Format('Pathinfo: "%s" "%s"',[P,FReturnedPathInfo]));{$ENDIF}
   I:=Pos('/',P);
   If (I=0) then
     I:=Length(P)+1;
   Result:=Copy(P,1,I-1);
-  FProcessedPathInfo:=IncludeHTTPPathDelimiter(FProcessedPathInfo)+Result;
- {$ifdef CGIDEBUG}SendDebug(Format('Pathinfo: "%s" "%s" : %s',[P,FProcessedPathInfo,Result]));{$ENDIF}
+  FReturnedPathInfo:=IncludeHTTPPathDelimiter(FReturnedPathInfo)+Result;
+ {$ifdef CGIDEBUG}SendDebug(Format('Pathinfo: "%s" "%s" : %s',[P,FReturnedPathInfo,Result]));{$ENDIF}
 end;
 
 procedure TRequest.ParseFirstHeaderLine(const line: String);

+ 2455 - 0
packages/fcl-web/src/jsonrpc/Makefile

@@ -0,0 +1,2455 @@
+#
+# Don't edit, this file is generated by FPCMake Version 2.0.0 [2009/11/05]
+#
+default: all
+MAKEFILETARGETS=i386-linux i386-go32v2 i386-win32 i386-os2 i386-freebsd i386-beos i386-haiku i386-netbsd i386-solaris i386-qnx i386-netware i386-openbsd i386-wdosx i386-darwin i386-emx i386-watcom i386-netwlibc i386-wince i386-embedded i386-symbian m68k-linux m68k-freebsd m68k-netbsd m68k-amiga m68k-atari m68k-openbsd m68k-palmos m68k-embedded powerpc-linux powerpc-netbsd powerpc-amiga powerpc-macos powerpc-darwin powerpc-morphos powerpc-embedded sparc-linux sparc-netbsd sparc-solaris sparc-embedded x86_64-linux x86_64-freebsd x86_64-darwin x86_64-win64 x86_64-embedded arm-linux arm-palmos arm-darwin arm-wince arm-gba arm-nds arm-embedded arm-symbian powerpc64-linux powerpc64-darwin powerpc64-embedded avr-embedded armeb-linux armeb-embedded
+BSDs = freebsd netbsd openbsd darwin
+UNIXs = linux $(BSDs) solaris qnx
+LIMIT83fs = go32v2 os2 emx watcom
+OSNeedsComspecToRunBatch = go32v2 watcom
+FORCE:
+.PHONY: FORCE
+override PATH:=$(patsubst %/,%,$(subst \,/,$(PATH)))
+ifneq ($(findstring darwin,$(OSTYPE)),)
+inUnix=1 #darwin
+SEARCHPATH:=$(filter-out .,$(subst :, ,$(PATH)))
+else
+ifeq ($(findstring ;,$(PATH)),)
+inUnix=1
+SEARCHPATH:=$(filter-out .,$(subst :, ,$(PATH)))
+else
+SEARCHPATH:=$(subst ;, ,$(PATH))
+endif
+endif
+SEARCHPATH+=$(patsubst %/,%,$(subst \,/,$(dir $(MAKE))))
+PWD:=$(strip $(wildcard $(addsuffix /pwd.exe,$(SEARCHPATH))))
+ifeq ($(PWD),)
+PWD:=$(strip $(wildcard $(addsuffix /pwd,$(SEARCHPATH))))
+ifeq ($(PWD),)
+$(error You need the GNU utils package to use this Makefile)
+else
+PWD:=$(firstword $(PWD))
+SRCEXEEXT=
+endif
+else
+PWD:=$(firstword $(PWD))
+SRCEXEEXT=.exe
+endif
+ifndef inUnix
+ifeq ($(OS),Windows_NT)
+inWinNT=1
+else
+ifdef OS2_SHELL
+inOS2=1
+endif
+endif
+else
+ifneq ($(findstring cygdrive,$(PATH)),)
+inCygWin=1
+endif
+endif
+ifdef inUnix
+SRCBATCHEXT=.sh
+else
+ifdef inOS2
+SRCBATCHEXT=.cmd
+else
+SRCBATCHEXT=.bat
+endif
+endif
+ifdef COMSPEC
+ifneq ($(findstring $(OS_SOURCE),$(OSNeedsComspecToRunBatch)),)
+RUNBATCH=$(COMSPEC) /C
+endif
+endif
+ifdef inUnix
+PATHSEP=/
+else
+PATHSEP:=$(subst /,\,/)
+ifdef inCygWin
+PATHSEP=/
+endif
+endif
+ifdef PWD
+BASEDIR:=$(subst \,/,$(shell $(PWD)))
+ifdef inCygWin
+ifneq ($(findstring /cygdrive/,$(BASEDIR)),)
+BASENODIR:=$(patsubst /cygdrive%,%,$(BASEDIR))
+BASEDRIVE:=$(firstword $(subst /, ,$(BASENODIR)))
+BASEDIR:=$(subst /cygdrive/$(BASEDRIVE)/,$(BASEDRIVE):/,$(BASEDIR))
+endif
+endif
+else
+BASEDIR=.
+endif
+ifdef inOS2
+ifndef ECHO
+ECHO:=$(strip $(wildcard $(addsuffix /gecho$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(ECHO),)
+ECHO:=$(strip $(wildcard $(addsuffix /echo$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(ECHO),)
+ECHO=echo
+else
+ECHO:=$(firstword $(ECHO))
+endif
+else
+ECHO:=$(firstword $(ECHO))
+endif
+endif
+export ECHO
+endif
+override DEFAULT_FPCDIR=../../../..
+ifndef FPC
+ifdef PP
+FPC=$(PP)
+endif
+endif
+ifndef FPC
+FPCPROG:=$(strip $(wildcard $(addsuffix /fpc$(SRCEXEEXT),$(SEARCHPATH))))
+ifneq ($(FPCPROG),)
+FPCPROG:=$(firstword $(FPCPROG))
+ifneq ($(CPU_TARGET),)
+FPC:=$(shell $(FPCPROG) -P$(CPU_TARGET) -PB)
+else
+FPC:=$(shell $(FPCPROG) -PB)
+endif
+ifneq ($(findstring Error,$(FPC)),)
+override FPC=$(firstword $(strip $(wildcard $(addsuffix /ppc386$(SRCEXEEXT),$(SEARCHPATH)))))
+else
+ifeq ($(strip $(wildcard $(FPC))),)
+FPC:=$(firstword $(FPCPROG))
+endif
+endif
+else
+override FPC=$(firstword $(strip $(wildcard $(addsuffix /ppc386$(SRCEXEEXT),$(SEARCHPATH)))))
+endif
+endif
+override FPC:=$(subst $(SRCEXEEXT),,$(FPC))
+override FPC:=$(subst \,/,$(FPC))$(SRCEXEEXT)
+FOUNDFPC:=$(strip $(wildcard $(FPC)))
+ifeq ($(FOUNDFPC),)
+FOUNDFPC=$(strip $(wildcard $(addsuffix /$(FPC),$(SEARCHPATH))))
+ifeq ($(FOUNDFPC),)
+$(error Compiler $(FPC) not found)
+endif
+endif
+ifndef FPC_COMPILERINFO
+FPC_COMPILERINFO:=$(shell $(FPC) -iVSPTPSOTO)
+endif
+ifndef FPC_VERSION
+FPC_VERSION:=$(word 1,$(FPC_COMPILERINFO))
+endif
+export FPC FPC_VERSION FPC_COMPILERINFO
+unexport CHECKDEPEND ALLDEPENDENCIES
+ifndef CPU_TARGET
+ifdef CPU_TARGET_DEFAULT
+CPU_TARGET=$(CPU_TARGET_DEFAULT)
+endif
+endif
+ifndef OS_TARGET
+ifdef OS_TARGET_DEFAULT
+OS_TARGET=$(OS_TARGET_DEFAULT)
+endif
+endif
+ifneq ($(words $(FPC_COMPILERINFO)),5)
+FPC_COMPILERINFO+=$(shell $(FPC) -iSP)
+FPC_COMPILERINFO+=$(shell $(FPC) -iTP)
+FPC_COMPILERINFO+=$(shell $(FPC) -iSO)
+FPC_COMPILERINFO+=$(shell $(FPC) -iTO)
+endif
+ifndef CPU_SOURCE
+CPU_SOURCE:=$(word 2,$(FPC_COMPILERINFO))
+endif
+ifndef CPU_TARGET
+CPU_TARGET:=$(word 3,$(FPC_COMPILERINFO))
+endif
+ifndef OS_SOURCE
+OS_SOURCE:=$(word 4,$(FPC_COMPILERINFO))
+endif
+ifndef OS_TARGET
+OS_TARGET:=$(word 5,$(FPC_COMPILERINFO))
+endif
+FULL_TARGET=$(CPU_TARGET)-$(OS_TARGET)
+FULL_SOURCE=$(CPU_SOURCE)-$(OS_SOURCE)
+ifeq ($(CPU_TARGET),armeb)
+ARCH=arm
+override FPCOPT+=-Cb
+else
+ifeq ($(CPU_TARGET),armel)
+ARCH=arm
+override FPCOPT+=-CaEABI
+else
+ARCH=$(CPU_TARGET)
+endif
+endif
+ifneq ($(findstring $(OS_SOURCE),$(LIMIT83fs)),)
+TARGETSUFFIX=$(OS_TARGET)
+SOURCESUFFIX=$(OS_SOURCE)
+else
+TARGETSUFFIX=$(FULL_TARGET)
+SOURCESUFFIX=$(FULL_SOURCE)
+endif
+ifneq ($(FULL_TARGET),$(FULL_SOURCE))
+CROSSCOMPILE=1
+endif
+ifeq ($(findstring makefile,$(MAKECMDGOALS)),)
+ifeq ($(findstring $(FULL_TARGET),$(MAKEFILETARGETS)),)
+$(error The Makefile doesn't support target $(FULL_TARGET), please run fpcmake first)
+endif
+endif
+ifneq ($(findstring $(OS_TARGET),$(BSDs)),)
+BSDhier=1
+endif
+ifeq ($(OS_TARGET),linux)
+linuxHier=1
+endif
+export OS_TARGET OS_SOURCE ARCH CPU_TARGET CPU_SOURCE FULL_TARGET FULL_SOURCE TARGETSUFFIX SOURCESUFFIX CROSSCOMPILE
+ifdef FPCDIR
+override FPCDIR:=$(subst \,/,$(FPCDIR))
+ifeq ($(wildcard $(addprefix $(FPCDIR)/,rtl units)),)
+override FPCDIR=wrong
+endif
+else
+override FPCDIR=wrong
+endif
+ifdef DEFAULT_FPCDIR
+ifeq ($(FPCDIR),wrong)
+override FPCDIR:=$(subst \,/,$(DEFAULT_FPCDIR))
+ifeq ($(wildcard $(addprefix $(FPCDIR)/,rtl units)),)
+override FPCDIR=wrong
+endif
+endif
+endif
+ifeq ($(FPCDIR),wrong)
+ifdef inUnix
+override FPCDIR=/usr/local/lib/fpc/$(FPC_VERSION)
+ifeq ($(wildcard $(FPCDIR)/units),)
+override FPCDIR=/usr/lib/fpc/$(FPC_VERSION)
+endif
+else
+override FPCDIR:=$(subst /$(FPC),,$(firstword $(strip $(wildcard $(addsuffix /$(FPC),$(SEARCHPATH))))))
+override FPCDIR:=$(FPCDIR)/..
+ifeq ($(wildcard $(addprefix $(FPCDIR)/,rtl units)),)
+override FPCDIR:=$(FPCDIR)/..
+ifeq ($(wildcard $(addprefix $(FPCDIR)/,rtl units)),)
+override FPCDIR:=$(BASEDIR)
+ifeq ($(wildcard $(addprefix $(FPCDIR)/,rtl units)),)
+override FPCDIR=c:/pp
+endif
+endif
+endif
+endif
+endif
+ifndef CROSSBINDIR
+CROSSBINDIR:=$(wildcard $(FPCDIR)/bin/$(TARGETSUFFIX))
+endif
+ifeq ($(OS_TARGET),darwin)
+ifeq ($(OS_SOURCE),darwin)
+DARWIN2DARWIN=1
+endif
+endif
+ifndef BINUTILSPREFIX
+ifndef CROSSBINDIR
+ifdef CROSSCOMPILE
+ifndef DARWIN2DARWIN
+BINUTILSPREFIX=$(CPU_TARGET)-$(OS_TARGET)-
+endif
+endif
+endif
+endif
+UNITSDIR:=$(wildcard $(FPCDIR)/units/$(TARGETSUFFIX))
+ifeq ($(UNITSDIR),)
+UNITSDIR:=$(wildcard $(FPCDIR)/units/$(OS_TARGET))
+endif
+PACKAGESDIR:=$(wildcard $(FPCDIR) $(FPCDIR)/packages $(FPCDIR)/packages/base $(FPCDIR)/packages/extra)
+override PACKAGE_NAME=fcl-web
+override PACKAGE_VERSION=2.5.1
+PACKAGEDIR_MAIN:=$(firstword $(subst /Makefile.fpc,,$(strip $(wildcard $(addsuffix /fcl-web/Makefile.fpc,$(PACKAGESDIR))))))
+ifeq ($(FULL_TARGET),i386-linux)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),i386-go32v2)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),i386-win32)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),i386-os2)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),i386-freebsd)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),i386-beos)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),i386-haiku)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),i386-netbsd)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),i386-solaris)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),i386-qnx)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),i386-netware)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),i386-openbsd)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),i386-wdosx)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),i386-darwin)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),i386-emx)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),i386-watcom)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),i386-netwlibc)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),i386-wince)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),i386-embedded)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),i386-symbian)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),m68k-linux)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),m68k-freebsd)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),m68k-netbsd)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),m68k-amiga)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),m68k-atari)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),m68k-openbsd)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),m68k-palmos)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),m68k-embedded)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),powerpc-linux)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),powerpc-netbsd)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),powerpc-amiga)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),powerpc-macos)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),powerpc-darwin)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),powerpc-morphos)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),powerpc-embedded)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),sparc-linux)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),sparc-netbsd)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),sparc-solaris)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),sparc-embedded)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),x86_64-linux)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),x86_64-freebsd)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),x86_64-darwin)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),x86_64-win64)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),x86_64-embedded)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),arm-linux)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),arm-palmos)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),arm-darwin)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),arm-wince)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),arm-gba)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),arm-nds)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),arm-embedded)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),arm-symbian)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),powerpc64-linux)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),powerpc64-darwin)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),powerpc64-embedded)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),avr-embedded)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),armeb-linux)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+ifeq ($(FULL_TARGET),armeb-embedded)
+override TARGET_UNITS+=fpjsonrpc  webjsonrpc fpextdirect
+endif
+override INSTALL_FPCPACKAGE=y
+ifeq ($(FULL_TARGET),i386-linux)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),i386-go32v2)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),i386-win32)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),i386-os2)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),i386-freebsd)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),i386-beos)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),i386-haiku)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),i386-netbsd)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),i386-solaris)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),i386-qnx)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),i386-netware)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),i386-openbsd)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),i386-wdosx)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),i386-darwin)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),i386-emx)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),i386-watcom)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),i386-netwlibc)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),i386-wince)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),i386-embedded)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),i386-symbian)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),m68k-linux)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),m68k-freebsd)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),m68k-netbsd)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),m68k-amiga)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),m68k-atari)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),m68k-openbsd)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),m68k-palmos)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),m68k-embedded)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),powerpc-linux)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),powerpc-netbsd)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),powerpc-amiga)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),powerpc-macos)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),powerpc-darwin)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),powerpc-morphos)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),powerpc-embedded)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),sparc-linux)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),sparc-netbsd)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),sparc-solaris)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),sparc-embedded)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),x86_64-linux)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),x86_64-freebsd)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),x86_64-darwin)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),x86_64-win64)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),x86_64-embedded)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),arm-linux)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),arm-palmos)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),arm-darwin)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),arm-wince)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),arm-gba)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),arm-nds)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),arm-embedded)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),arm-symbian)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),powerpc64-linux)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),powerpc64-darwin)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),powerpc64-embedded)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),avr-embedded)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),armeb-linux)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifeq ($(FULL_TARGET),armeb-embedded)
+override COMPILER_OPTIONS+=-S2h
+endif
+ifdef REQUIRE_UNITSDIR
+override UNITSDIR+=$(REQUIRE_UNITSDIR)
+endif
+ifdef REQUIRE_PACKAGESDIR
+override PACKAGESDIR+=$(REQUIRE_PACKAGESDIR)
+endif
+ifdef ZIPINSTALL
+ifneq ($(findstring $(OS_TARGET),$(UNIXs)),)
+UNIXHier=1
+endif
+else
+ifneq ($(findstring $(OS_SOURCE),$(UNIXs)),)
+UNIXHier=1
+endif
+endif
+ifndef INSTALL_PREFIX
+ifdef PREFIX
+INSTALL_PREFIX=$(PREFIX)
+endif
+endif
+ifndef INSTALL_PREFIX
+ifdef UNIXHier
+INSTALL_PREFIX=/usr/local
+else
+ifdef INSTALL_FPCPACKAGE
+INSTALL_BASEDIR:=/pp
+else
+INSTALL_BASEDIR:=/$(PACKAGE_NAME)
+endif
+endif
+endif
+export INSTALL_PREFIX
+ifdef INSTALL_FPCSUBDIR
+export INSTALL_FPCSUBDIR
+endif
+ifndef DIST_DESTDIR
+DIST_DESTDIR:=$(BASEDIR)
+endif
+export DIST_DESTDIR
+ifndef COMPILER_UNITTARGETDIR
+ifdef PACKAGEDIR_MAIN
+COMPILER_UNITTARGETDIR=$(PACKAGEDIR_MAIN)/units/$(TARGETSUFFIX)
+else
+COMPILER_UNITTARGETDIR=units/$(TARGETSUFFIX)
+endif
+endif
+ifndef COMPILER_TARGETDIR
+COMPILER_TARGETDIR=.
+endif
+ifndef INSTALL_BASEDIR
+ifdef UNIXHier
+ifdef INSTALL_FPCPACKAGE
+INSTALL_BASEDIR:=$(INSTALL_PREFIX)/lib/fpc/$(FPC_VERSION)
+else
+INSTALL_BASEDIR:=$(INSTALL_PREFIX)/lib/$(PACKAGE_NAME)
+endif
+else
+INSTALL_BASEDIR:=$(INSTALL_PREFIX)
+endif
+endif
+ifndef INSTALL_BINDIR
+ifdef UNIXHier
+INSTALL_BINDIR:=$(INSTALL_PREFIX)/bin
+else
+INSTALL_BINDIR:=$(INSTALL_BASEDIR)/bin
+ifdef INSTALL_FPCPACKAGE
+ifdef CROSSCOMPILE
+ifdef CROSSINSTALL
+INSTALL_BINDIR:=$(INSTALL_BINDIR)/$(SOURCESUFFIX)
+else
+INSTALL_BINDIR:=$(INSTALL_BINDIR)/$(TARGETSUFFIX)
+endif
+else
+INSTALL_BINDIR:=$(INSTALL_BINDIR)/$(TARGETSUFFIX)
+endif
+endif
+endif
+endif
+ifndef INSTALL_UNITDIR
+INSTALL_UNITDIR:=$(INSTALL_BASEDIR)/units/$(TARGETSUFFIX)
+ifdef INSTALL_FPCPACKAGE
+ifdef PACKAGE_NAME
+INSTALL_UNITDIR:=$(INSTALL_UNITDIR)/$(PACKAGE_NAME)
+endif
+endif
+endif
+ifndef INSTALL_LIBDIR
+ifdef UNIXHier
+INSTALL_LIBDIR:=$(INSTALL_PREFIX)/lib
+else
+INSTALL_LIBDIR:=$(INSTALL_UNITDIR)
+endif
+endif
+ifndef INSTALL_SOURCEDIR
+ifdef UNIXHier
+ifdef BSDhier
+SRCPREFIXDIR=share/src
+else
+ifdef linuxHier
+SRCPREFIXDIR=share/src
+else
+SRCPREFIXDIR=src
+endif
+endif
+ifdef INSTALL_FPCPACKAGE
+ifdef INSTALL_FPCSUBDIR
+INSTALL_SOURCEDIR:=$(INSTALL_PREFIX)/$(SRCPREFIXDIR)/fpc-$(FPC_VERSION)/$(INSTALL_FPCSUBDIR)/$(PACKAGE_NAME)
+else
+INSTALL_SOURCEDIR:=$(INSTALL_PREFIX)/$(SRCPREFIXDIR)/fpc-$(FPC_VERSION)/$(PACKAGE_NAME)
+endif
+else
+INSTALL_SOURCEDIR:=$(INSTALL_PREFIX)/$(SRCPREFIXDIR)/$(PACKAGE_NAME)-$(PACKAGE_VERSION)
+endif
+else
+ifdef INSTALL_FPCPACKAGE
+ifdef INSTALL_FPCSUBDIR
+INSTALL_SOURCEDIR:=$(INSTALL_BASEDIR)/source/$(INSTALL_FPCSUBDIR)/$(PACKAGE_NAME)
+else
+INSTALL_SOURCEDIR:=$(INSTALL_BASEDIR)/source/$(PACKAGE_NAME)
+endif
+else
+INSTALL_SOURCEDIR:=$(INSTALL_BASEDIR)/source
+endif
+endif
+endif
+ifndef INSTALL_DOCDIR
+ifdef UNIXHier
+ifdef BSDhier
+DOCPREFIXDIR=share/doc
+else
+ifdef linuxHier
+DOCPREFIXDIR=share/doc
+else
+DOCPREFIXDIR=doc
+endif
+endif
+ifdef INSTALL_FPCPACKAGE
+INSTALL_DOCDIR:=$(INSTALL_PREFIX)/$(DOCPREFIXDIR)/fpc-$(FPC_VERSION)/$(PACKAGE_NAME)
+else
+INSTALL_DOCDIR:=$(INSTALL_PREFIX)/$(DOCPREFIXDIR)/$(PACKAGE_NAME)-$(PACKAGE_VERSION)
+endif
+else
+ifdef INSTALL_FPCPACKAGE
+INSTALL_DOCDIR:=$(INSTALL_BASEDIR)/doc/$(PACKAGE_NAME)
+else
+INSTALL_DOCDIR:=$(INSTALL_BASEDIR)/doc
+endif
+endif
+endif
+ifndef INSTALL_EXAMPLEDIR
+ifdef UNIXHier
+ifdef INSTALL_FPCPACKAGE
+ifdef BSDhier
+INSTALL_EXAMPLEDIR:=$(INSTALL_PREFIX)/share/examples/fpc-$(FPC_VERSION)/$(PACKAGE_NAME)
+else
+ifdef linuxHier
+INSTALL_EXAMPLEDIR:=$(INSTALL_DOCDIR)/examples
+else
+INSTALL_EXAMPLEDIR:=$(INSTALL_PREFIX)/doc/fpc-$(FPC_VERSION)/examples/$(PACKAGE_NAME)
+endif
+endif
+else
+ifdef BSDhier
+INSTALL_EXAMPLEDIR:=$(INSTALL_PREFIX)/share/examples/$(PACKAGE_NAME)-$(PACKAGE_VERSION)
+else
+ifdef linuxHier
+INSTALL_EXAMPLEDIR:=$(INSTALL_DOCDIR)/examples/$(PACKAGE_NAME)-$(PACKAGE_VERSION)
+else
+INSTALL_EXAMPLEDIR:=$(INSTALL_PREFIX)/doc/$(PACKAGE_NAME)-$(PACKAGE_VERSION)
+endif
+endif
+endif
+else
+ifdef INSTALL_FPCPACKAGE
+INSTALL_EXAMPLEDIR:=$(INSTALL_BASEDIR)/examples/$(PACKAGE_NAME)
+else
+INSTALL_EXAMPLEDIR:=$(INSTALL_BASEDIR)/examples
+endif
+endif
+endif
+ifndef INSTALL_DATADIR
+INSTALL_DATADIR=$(INSTALL_BASEDIR)
+endif
+ifndef INSTALL_SHAREDDIR
+INSTALL_SHAREDDIR=$(INSTALL_PREFIX)/lib
+endif
+ifdef CROSSCOMPILE
+ifndef CROSSBINDIR
+CROSSBINDIR:=$(wildcard $(CROSSTARGETDIR)/bin/$(SOURCESUFFIX))
+ifeq ($(CROSSBINDIR),)
+CROSSBINDIR:=$(wildcard $(INSTALL_BASEDIR)/cross/$(TARGETSUFFIX)/bin/$(FULL_SOURCE))
+endif
+endif
+else
+CROSSBINDIR=
+endif
+ifeq ($(OS_SOURCE),linux)
+ifndef GCCLIBDIR
+ifeq ($(CPU_TARGET),i386)
+ifneq ($(findstring x86_64,$(shell uname -a)),)
+ifeq ($(BINUTILSPREFIX),)
+GCCLIBDIR:=$(shell dirname `gcc -m32 -print-libgcc-file-name`)
+endif
+endif
+endif
+ifeq ($(CPU_TARGET),powerpc64)
+ifeq ($(BINUTILSPREFIX),)
+GCCLIBDIR:=$(shell dirname `gcc -m64 -print-libgcc-file-name`)
+endif
+endif
+endif
+ifndef GCCLIBDIR
+CROSSGCC=$(strip $(wildcard $(addsuffix /$(BINUTILSPREFIX)gcc$(SRCEXEEXT),$(SEARCHPATH))))
+ifneq ($(CROSSGCC),)
+GCCLIBDIR:=$(shell dirname `$(CROSSGCC) -print-libgcc-file-name`)
+endif
+endif
+ifndef OTHERLIBDIR
+OTHERLIBDIR:=$(shell grep -v "^\#" /etc/ld.so.conf | awk '{ ORS=" "; print $1 }')
+endif
+endif
+ifdef inUnix
+ifeq ($(OS_SOURCE),netbsd)
+OTHERLIBDIR+=/usr/pkg/lib
+endif
+export GCCLIBDIR OTHERLIB
+endif
+BATCHEXT=.bat
+LOADEREXT=.as
+EXEEXT=.exe
+PPLEXT=.ppl
+PPUEXT=.ppu
+OEXT=.o
+ASMEXT=.s
+SMARTEXT=.sl
+STATICLIBEXT=.a
+SHAREDLIBEXT=.so
+SHAREDLIBPREFIX=libfp
+STATICLIBPREFIX=libp
+IMPORTLIBPREFIX=libimp
+RSTEXT=.rst
+ifeq ($(findstring 1.0.,$(FPC_VERSION)),)
+ifeq ($(OS_TARGET),go32v1)
+STATICLIBPREFIX=
+SHORTSUFFIX=v1
+endif
+ifeq ($(OS_TARGET),go32v2)
+STATICLIBPREFIX=
+SHORTSUFFIX=dos
+IMPORTLIBPREFIX=
+endif
+ifeq ($(OS_TARGET),watcom)
+STATICLIBPREFIX=
+OEXT=.obj
+ASMEXT=.asm
+SHAREDLIBEXT=.dll
+SHORTSUFFIX=wat
+IMPORTLIBPREFIX=
+endif
+ifeq ($(OS_TARGET),linux)
+BATCHEXT=.sh
+EXEEXT=
+HASSHAREDLIB=1
+SHORTSUFFIX=lnx
+endif
+ifeq ($(OS_TARGET),freebsd)
+BATCHEXT=.sh
+EXEEXT=
+HASSHAREDLIB=1
+SHORTSUFFIX=fbs
+endif
+ifeq ($(OS_TARGET),netbsd)
+BATCHEXT=.sh
+EXEEXT=
+HASSHAREDLIB=1
+SHORTSUFFIX=nbs
+endif
+ifeq ($(OS_TARGET),openbsd)
+BATCHEXT=.sh
+EXEEXT=
+HASSHAREDLIB=1
+SHORTSUFFIX=obs
+endif
+ifeq ($(OS_TARGET),win32)
+SHAREDLIBEXT=.dll
+SHORTSUFFIX=w32
+endif
+ifeq ($(OS_TARGET),os2)
+BATCHEXT=.cmd
+AOUTEXT=.out
+STATICLIBPREFIX=
+SHAREDLIBEXT=.dll
+SHORTSUFFIX=os2
+ECHO=echo
+IMPORTLIBPREFIX=
+endif
+ifeq ($(OS_TARGET),emx)
+BATCHEXT=.cmd
+AOUTEXT=.out
+STATICLIBPREFIX=
+SHAREDLIBEXT=.dll
+SHORTSUFFIX=emx
+ECHO=echo
+IMPORTLIBPREFIX=
+endif
+ifeq ($(OS_TARGET),amiga)
+EXEEXT=
+SHAREDLIBEXT=.library
+SHORTSUFFIX=amg
+endif
+ifeq ($(OS_TARGET),morphos)
+EXEEXT=
+SHAREDLIBEXT=.library
+SHORTSUFFIX=mos
+endif
+ifeq ($(OS_TARGET),atari)
+EXEEXT=.ttp
+SHORTSUFFIX=ata
+endif
+ifeq ($(OS_TARGET),beos)
+BATCHEXT=.sh
+EXEEXT=
+SHORTSUFFIX=be
+endif
+ifeq ($(OS_TARGET),haiku)
+BATCHEXT=.sh
+EXEEXT=
+SHORTSUFFIX=hai
+endif
+ifeq ($(OS_TARGET),solaris)
+BATCHEXT=.sh
+EXEEXT=
+SHORTSUFFIX=sun
+endif
+ifeq ($(OS_TARGET),qnx)
+BATCHEXT=.sh
+EXEEXT=
+SHORTSUFFIX=qnx
+endif
+ifeq ($(OS_TARGET),netware)
+EXEEXT=.nlm
+STATICLIBPREFIX=
+SHORTSUFFIX=nw
+IMPORTLIBPREFIX=imp
+endif
+ifeq ($(OS_TARGET),netwlibc)
+EXEEXT=.nlm
+STATICLIBPREFIX=
+SHORTSUFFIX=nwl
+IMPORTLIBPREFIX=imp
+endif
+ifeq ($(OS_TARGET),macos)
+BATCHEXT=
+EXEEXT=
+DEBUGSYMEXT=.xcoff
+SHORTSUFFIX=mac
+IMPORTLIBPREFIX=imp
+endif
+ifeq ($(OS_TARGET),darwin)
+BATCHEXT=.sh
+EXEEXT=
+HASSHAREDLIB=1
+SHORTSUFFIX=dwn
+endif
+ifeq ($(OS_TARGET),gba)
+EXEEXT=.gba
+SHAREDLIBEXT=.so
+SHORTSUFFIX=gba
+endif
+ifeq ($(OS_TARGET),symbian)
+SHAREDLIBEXT=.dll
+SHORTSUFFIX=symbian
+endif
+else
+ifeq ($(OS_TARGET),go32v1)
+PPUEXT=.pp1
+OEXT=.o1
+ASMEXT=.s1
+SMARTEXT=.sl1
+STATICLIBEXT=.a1
+SHAREDLIBEXT=.so1
+STATICLIBPREFIX=
+SHORTSUFFIX=v1
+IMPORTLIBPREFIX=
+endif
+ifeq ($(OS_TARGET),go32v2)
+STATICLIBPREFIX=
+SHORTSUFFIX=dos
+IMPORTLIBPREFIX=
+endif
+ifeq ($(OS_TARGET),watcom)
+STATICLIBPREFIX=
+SHORTSUFFIX=wat
+IMPORTLIBPREFIX=
+endif
+ifeq ($(OS_TARGET),linux)
+BATCHEXT=.sh
+EXEEXT=
+HASSHAREDLIB=1
+SHORTSUFFIX=lnx
+endif
+ifeq ($(OS_TARGET),freebsd)
+BATCHEXT=.sh
+EXEEXT=
+HASSHAREDLIB=1
+SHORTSUFFIX=fbs
+endif
+ifeq ($(OS_TARGET),netbsd)
+BATCHEXT=.sh
+EXEEXT=
+HASSHAREDLIB=1
+SHORTSUFFIX=nbs
+endif
+ifeq ($(OS_TARGET),openbsd)
+BATCHEXT=.sh
+EXEEXT=
+HASSHAREDLIB=1
+SHORTSUFFIX=obs
+endif
+ifeq ($(OS_TARGET),win32)
+PPUEXT=.ppw
+OEXT=.ow
+ASMEXT=.sw
+SMARTEXT=.slw
+STATICLIBEXT=.aw
+SHAREDLIBEXT=.dll
+SHORTSUFFIX=w32
+endif
+ifeq ($(OS_TARGET),os2)
+BATCHEXT=.cmd
+PPUEXT=.ppo
+ASMEXT=.so2
+OEXT=.oo2
+AOUTEXT=.out
+SMARTEXT=.sl2
+STATICLIBPREFIX=
+STATICLIBEXT=.ao2
+SHAREDLIBEXT=.dll
+SHORTSUFFIX=os2
+ECHO=echo
+IMPORTLIBPREFIX=
+endif
+ifeq ($(OS_TARGET),amiga)
+EXEEXT=
+PPUEXT=.ppu
+ASMEXT=.s
+OEXT=.o
+SMARTEXT=.sl
+STATICLIBEXT=.a
+SHAREDLIBEXT=.library
+SHORTSUFFIX=amg
+endif
+ifeq ($(OS_TARGET),atari)
+PPUEXT=.ppu
+ASMEXT=.s
+OEXT=.o
+SMARTEXT=.sl
+STATICLIBEXT=.a
+EXEEXT=.ttp
+SHORTSUFFIX=ata
+endif
+ifeq ($(OS_TARGET),beos)
+BATCHEXT=.sh
+PPUEXT=.ppu
+ASMEXT=.s
+OEXT=.o
+SMARTEXT=.sl
+STATICLIBEXT=.a
+EXEEXT=
+SHORTSUFFIX=be
+endif
+ifeq ($(OS_TARGET),solaris)
+BATCHEXT=.sh
+PPUEXT=.ppu
+ASMEXT=.s
+OEXT=.o
+SMARTEXT=.sl
+STATICLIBEXT=.a
+EXEEXT=
+SHORTSUFFIX=sun
+endif
+ifeq ($(OS_TARGET),qnx)
+BATCHEXT=.sh
+PPUEXT=.ppu
+ASMEXT=.s
+OEXT=.o
+SMARTEXT=.sl
+STATICLIBEXT=.a
+EXEEXT=
+SHORTSUFFIX=qnx
+endif
+ifeq ($(OS_TARGET),netware)
+STATICLIBPREFIX=
+PPUEXT=.ppu
+OEXT=.o
+ASMEXT=.s
+SMARTEXT=.sl
+STATICLIBEXT=.a
+SHAREDLIBEXT=.nlm
+EXEEXT=.nlm
+SHORTSUFFIX=nw
+IMPORTLIBPREFIX=imp
+endif
+ifeq ($(OS_TARGET),netwlibc)
+STATICLIBPREFIX=
+PPUEXT=.ppu
+OEXT=.o
+ASMEXT=.s
+SMARTEXT=.sl
+STATICLIBEXT=.a
+SHAREDLIBEXT=.nlm
+EXEEXT=.nlm
+SHORTSUFFIX=nwl
+IMPORTLIBPREFIX=imp
+endif
+ifeq ($(OS_TARGET),macos)
+BATCHEXT=
+PPUEXT=.ppu
+ASMEXT=.s
+OEXT=.o
+SMARTEXT=.sl
+STATICLIBEXT=.a
+EXEEXT=
+DEBUGSYMEXT=.xcoff
+SHORTSUFFIX=mac
+IMPORTLIBPREFIX=imp
+endif
+endif
+ifneq ($(findstring $(OS_SOURCE),$(LIMIT83fs)),)
+FPCMADE=fpcmade.$(SHORTSUFFIX)
+ZIPSUFFIX=$(SHORTSUFFIX)
+ZIPCROSSPREFIX=
+ZIPSOURCESUFFIX=src
+ZIPEXAMPLESUFFIX=exm
+else
+FPCMADE=fpcmade.$(TARGETSUFFIX)
+ZIPSOURCESUFFIX=.source
+ZIPEXAMPLESUFFIX=.examples
+ifdef CROSSCOMPILE
+ZIPSUFFIX=.$(SOURCESUFFIX)
+ZIPCROSSPREFIX=$(TARGETSUFFIX)-
+else
+ZIPSUFFIX=.$(TARGETSUFFIX)
+ZIPCROSSPREFIX=
+endif
+endif
+ifndef ECHO
+ECHO:=$(strip $(wildcard $(addsuffix /gecho$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(ECHO),)
+ECHO:=$(strip $(wildcard $(addsuffix /echo$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(ECHO),)
+ECHO= __missing_command_ECHO
+else
+ECHO:=$(firstword $(ECHO))
+endif
+else
+ECHO:=$(firstword $(ECHO))
+endif
+endif
+export ECHO
+ifndef DATE
+DATE:=$(strip $(wildcard $(addsuffix /gdate$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(DATE),)
+DATE:=$(strip $(wildcard $(addsuffix /date$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(DATE),)
+DATE= __missing_command_DATE
+else
+DATE:=$(firstword $(DATE))
+endif
+else
+DATE:=$(firstword $(DATE))
+endif
+endif
+export DATE
+ifndef GINSTALL
+GINSTALL:=$(strip $(wildcard $(addsuffix /ginstall$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(GINSTALL),)
+GINSTALL:=$(strip $(wildcard $(addsuffix /install$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(GINSTALL),)
+GINSTALL= __missing_command_GINSTALL
+else
+GINSTALL:=$(firstword $(GINSTALL))
+endif
+else
+GINSTALL:=$(firstword $(GINSTALL))
+endif
+endif
+export GINSTALL
+ifndef CPPROG
+CPPROG:=$(strip $(wildcard $(addsuffix /cp$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(CPPROG),)
+CPPROG= __missing_command_CPPROG
+else
+CPPROG:=$(firstword $(CPPROG))
+endif
+endif
+export CPPROG
+ifndef RMPROG
+RMPROG:=$(strip $(wildcard $(addsuffix /rm$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(RMPROG),)
+RMPROG= __missing_command_RMPROG
+else
+RMPROG:=$(firstword $(RMPROG))
+endif
+endif
+export RMPROG
+ifndef MVPROG
+MVPROG:=$(strip $(wildcard $(addsuffix /mv$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(MVPROG),)
+MVPROG= __missing_command_MVPROG
+else
+MVPROG:=$(firstword $(MVPROG))
+endif
+endif
+export MVPROG
+ifndef MKDIRPROG
+MKDIRPROG:=$(strip $(wildcard $(addsuffix /gmkdir$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(MKDIRPROG),)
+MKDIRPROG:=$(strip $(wildcard $(addsuffix /mkdir$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(MKDIRPROG),)
+MKDIRPROG= __missing_command_MKDIRPROG
+else
+MKDIRPROG:=$(firstword $(MKDIRPROG))
+endif
+else
+MKDIRPROG:=$(firstword $(MKDIRPROG))
+endif
+endif
+export MKDIRPROG
+ifndef ECHOREDIR
+ifndef inUnix
+ECHOREDIR=echo
+else
+ECHOREDIR=$(ECHO)
+endif
+endif
+ifndef COPY
+COPY:=$(CPPROG) -fp
+endif
+ifndef COPYTREE
+COPYTREE:=$(CPPROG) -Rfp
+endif
+ifndef MKDIRTREE
+MKDIRTREE:=$(MKDIRPROG) -p
+endif
+ifndef MOVE
+MOVE:=$(MVPROG) -f
+endif
+ifndef DEL
+DEL:=$(RMPROG) -f
+endif
+ifndef DELTREE
+DELTREE:=$(RMPROG) -rf
+endif
+ifndef INSTALL
+ifdef inUnix
+INSTALL:=$(GINSTALL) -c -m 644
+else
+INSTALL:=$(COPY)
+endif
+endif
+ifndef INSTALLEXE
+ifdef inUnix
+INSTALLEXE:=$(GINSTALL) -c -m 755
+else
+INSTALLEXE:=$(COPY)
+endif
+endif
+ifndef MKDIR
+MKDIR:=$(GINSTALL) -m 755 -d
+endif
+export ECHOREDIR COPY COPYTREE MOVE DEL DELTREE INSTALL INSTALLEXE MKDIR
+ifndef PPUMOVE
+PPUMOVE:=$(strip $(wildcard $(addsuffix /ppumove$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(PPUMOVE),)
+PPUMOVE= __missing_command_PPUMOVE
+else
+PPUMOVE:=$(firstword $(PPUMOVE))
+endif
+endif
+export PPUMOVE
+ifndef FPCMAKE
+FPCMAKE:=$(strip $(wildcard $(addsuffix /fpcmake$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(FPCMAKE),)
+FPCMAKE= __missing_command_FPCMAKE
+else
+FPCMAKE:=$(firstword $(FPCMAKE))
+endif
+endif
+export FPCMAKE
+ifndef ZIPPROG
+ZIPPROG:=$(strip $(wildcard $(addsuffix /zip$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(ZIPPROG),)
+ZIPPROG= __missing_command_ZIPPROG
+else
+ZIPPROG:=$(firstword $(ZIPPROG))
+endif
+endif
+export ZIPPROG
+ifndef TARPROG
+TARPROG:=$(strip $(wildcard $(addsuffix /gtar$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(TARPROG),)
+TARPROG:=$(strip $(wildcard $(addsuffix /tar$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(TARPROG),)
+TARPROG= __missing_command_TARPROG
+else
+TARPROG:=$(firstword $(TARPROG))
+endif
+else
+TARPROG:=$(firstword $(TARPROG))
+endif
+endif
+export TARPROG
+ASNAME=$(BINUTILSPREFIX)as
+LDNAME=$(BINUTILSPREFIX)ld
+ARNAME=$(BINUTILSPREFIX)ar
+RCNAME=$(BINUTILSPREFIX)rc
+ifneq ($(findstring 1.0.,$(FPC_VERSION)),)
+ifeq ($(OS_TARGET),win32)
+ifeq ($(CROSSBINDIR),)
+ASNAME=asw
+LDNAME=ldw
+ARNAME=arw
+endif
+endif
+endif
+ifndef ASPROG
+ifdef CROSSBINDIR
+ASPROG=$(CROSSBINDIR)/$(ASNAME)$(SRCEXEEXT)
+else
+ASPROG=$(ASNAME)
+endif
+endif
+ifndef LDPROG
+ifdef CROSSBINDIR
+LDPROG=$(CROSSBINDIR)/$(LDNAME)$(SRCEXEEXT)
+else
+LDPROG=$(LDNAME)
+endif
+endif
+ifndef RCPROG
+ifdef CROSSBINDIR
+RCPROG=$(CROSSBINDIR)/$(RCNAME)$(SRCEXEEXT)
+else
+RCPROG=$(RCNAME)
+endif
+endif
+ifndef ARPROG
+ifdef CROSSBINDIR
+ARPROG=$(CROSSBINDIR)/$(ARNAME)$(SRCEXEEXT)
+else
+ARPROG=$(ARNAME)
+endif
+endif
+AS=$(ASPROG)
+LD=$(LDPROG)
+RC=$(RCPROG)
+AR=$(ARPROG)
+PPAS=ppas$(SRCBATCHEXT)
+ifdef inUnix
+LDCONFIG=ldconfig
+else
+LDCONFIG=
+endif
+ifdef DATE
+DATESTR:=$(shell $(DATE) +%Y%m%d)
+else
+DATESTR=
+endif
+ifndef UPXPROG
+ifeq ($(OS_TARGET),go32v2)
+UPXPROG:=1
+endif
+ifeq ($(OS_TARGET),win32)
+UPXPROG:=1
+endif
+ifdef UPXPROG
+UPXPROG:=$(strip $(wildcard $(addsuffix /upx$(SRCEXEEXT),$(SEARCHPATH))))
+ifeq ($(UPXPROG),)
+UPXPROG=
+else
+UPXPROG:=$(firstword $(UPXPROG))
+endif
+else
+UPXPROG=
+endif
+endif
+export UPXPROG
+ZIPOPT=-9
+ZIPEXT=.zip
+ifeq ($(USETAR),bz2)
+TAROPT=vj
+TAREXT=.tar.bz2
+else
+TAROPT=vz
+TAREXT=.tar.gz
+endif
+override REQUIRE_PACKAGES=rtl fcl-base fcl-xml fcl-json
+ifeq ($(FULL_TARGET),i386-linux)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_ICONVENC=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-go32v2)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-win32)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-os2)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-freebsd)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_ICONVENC=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-beos)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_ICONVENC=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-haiku)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_ICONVENC=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-netbsd)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-solaris)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-qnx)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-netware)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-openbsd)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-wdosx)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-darwin)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_UNIVINT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_ICONVENC=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-emx)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-watcom)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-netwlibc)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-wince)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),i386-symbian)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),m68k-linux)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_ICONVENC=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),m68k-freebsd)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_ICONVENC=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),m68k-netbsd)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),m68k-amiga)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),m68k-atari)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),m68k-openbsd)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),m68k-palmos)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),m68k-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),powerpc-linux)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_ICONVENC=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),powerpc-netbsd)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),powerpc-amiga)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),powerpc-macos)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),powerpc-darwin)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_UNIVINT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_ICONVENC=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),powerpc-morphos)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),powerpc-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),sparc-linux)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_ICONVENC=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),sparc-netbsd)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),sparc-solaris)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),sparc-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),x86_64-linux)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_ICONVENC=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),x86_64-freebsd)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_ICONVENC=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),x86_64-darwin)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_UNIVINT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_ICONVENC=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),x86_64-win64)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),x86_64-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),arm-linux)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_ICONVENC=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),arm-palmos)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),arm-darwin)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_UNIVINT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_ICONVENC=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),arm-wince)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),arm-gba)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),arm-nds)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),arm-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),arm-symbian)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),powerpc64-linux)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_ICONVENC=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),powerpc64-darwin)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_UNIVINT=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_ICONVENC=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),powerpc64-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),avr-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),armeb-linux)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_ICONVENC=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifeq ($(FULL_TARGET),armeb-embedded)
+REQUIRE_PACKAGES_RTL=1
+REQUIRE_PACKAGES_FCL-BASE=1
+REQUIRE_PACKAGES_FCL-XML=1
+REQUIRE_PACKAGES_FCL-JSON=1
+endif
+ifdef REQUIRE_PACKAGES_RTL
+PACKAGEDIR_RTL:=$(firstword $(subst /Makefile.fpc,,$(strip $(wildcard $(addsuffix /rtl/Makefile.fpc,$(PACKAGESDIR))))))
+ifneq ($(PACKAGEDIR_RTL),)
+ifneq ($(wildcard $(PACKAGEDIR_RTL)/units/$(TARGETSUFFIX)),)
+UNITDIR_RTL=$(PACKAGEDIR_RTL)/units/$(TARGETSUFFIX)
+else
+UNITDIR_RTL=$(PACKAGEDIR_RTL)
+endif
+ifdef CHECKDEPEND
+$(PACKAGEDIR_RTL)/$(FPCMADE):
+	$(MAKE) -C $(PACKAGEDIR_RTL) $(FPCMADE)
+override ALLDEPENDENCIES+=$(PACKAGEDIR_RTL)/$(FPCMADE)
+endif
+else
+PACKAGEDIR_RTL=
+UNITDIR_RTL:=$(subst /Package.fpc,,$(strip $(wildcard $(addsuffix /rtl/Package.fpc,$(UNITSDIR)))))
+ifneq ($(UNITDIR_RTL),)
+UNITDIR_RTL:=$(firstword $(UNITDIR_RTL))
+else
+UNITDIR_RTL=
+endif
+endif
+ifdef UNITDIR_RTL
+override COMPILER_UNITDIR+=$(UNITDIR_RTL)
+endif
+endif
+ifdef REQUIRE_PACKAGES_FCL-BASE
+PACKAGEDIR_FCL-BASE:=$(firstword $(subst /Makefile.fpc,,$(strip $(wildcard $(addsuffix /fcl-base/Makefile.fpc,$(PACKAGESDIR))))))
+ifneq ($(PACKAGEDIR_FCL-BASE),)
+ifneq ($(wildcard $(PACKAGEDIR_FCL-BASE)/units/$(TARGETSUFFIX)),)
+UNITDIR_FCL-BASE=$(PACKAGEDIR_FCL-BASE)/units/$(TARGETSUFFIX)
+else
+UNITDIR_FCL-BASE=$(PACKAGEDIR_FCL-BASE)
+endif
+ifdef CHECKDEPEND
+$(PACKAGEDIR_FCL-BASE)/$(FPCMADE):
+	$(MAKE) -C $(PACKAGEDIR_FCL-BASE) $(FPCMADE)
+override ALLDEPENDENCIES+=$(PACKAGEDIR_FCL-BASE)/$(FPCMADE)
+endif
+else
+PACKAGEDIR_FCL-BASE=
+UNITDIR_FCL-BASE:=$(subst /Package.fpc,,$(strip $(wildcard $(addsuffix /fcl-base/Package.fpc,$(UNITSDIR)))))
+ifneq ($(UNITDIR_FCL-BASE),)
+UNITDIR_FCL-BASE:=$(firstword $(UNITDIR_FCL-BASE))
+else
+UNITDIR_FCL-BASE=
+endif
+endif
+ifdef UNITDIR_FCL-BASE
+override COMPILER_UNITDIR+=$(UNITDIR_FCL-BASE)
+endif
+endif
+ifdef REQUIRE_PACKAGES_ICONVENC
+PACKAGEDIR_ICONVENC:=$(firstword $(subst /Makefile.fpc,,$(strip $(wildcard $(addsuffix /iconvenc/Makefile.fpc,$(PACKAGESDIR))))))
+ifneq ($(PACKAGEDIR_ICONVENC),)
+ifneq ($(wildcard $(PACKAGEDIR_ICONVENC)/units/$(TARGETSUFFIX)),)
+UNITDIR_ICONVENC=$(PACKAGEDIR_ICONVENC)/units/$(TARGETSUFFIX)
+else
+UNITDIR_ICONVENC=$(PACKAGEDIR_ICONVENC)
+endif
+ifdef CHECKDEPEND
+$(PACKAGEDIR_ICONVENC)/$(FPCMADE):
+	$(MAKE) -C $(PACKAGEDIR_ICONVENC) $(FPCMADE)
+override ALLDEPENDENCIES+=$(PACKAGEDIR_ICONVENC)/$(FPCMADE)
+endif
+else
+PACKAGEDIR_ICONVENC=
+UNITDIR_ICONVENC:=$(subst /Package.fpc,,$(strip $(wildcard $(addsuffix /iconvenc/Package.fpc,$(UNITSDIR)))))
+ifneq ($(UNITDIR_ICONVENC),)
+UNITDIR_ICONVENC:=$(firstword $(UNITDIR_ICONVENC))
+else
+UNITDIR_ICONVENC=
+endif
+endif
+ifdef UNITDIR_ICONVENC
+override COMPILER_UNITDIR+=$(UNITDIR_ICONVENC)
+endif
+endif
+ifdef REQUIRE_PACKAGES_FCL-XML
+PACKAGEDIR_FCL-XML:=$(firstword $(subst /Makefile.fpc,,$(strip $(wildcard $(addsuffix /fcl-xml/Makefile.fpc,$(PACKAGESDIR))))))
+ifneq ($(PACKAGEDIR_FCL-XML),)
+ifneq ($(wildcard $(PACKAGEDIR_FCL-XML)/units/$(TARGETSUFFIX)),)
+UNITDIR_FCL-XML=$(PACKAGEDIR_FCL-XML)/units/$(TARGETSUFFIX)
+else
+UNITDIR_FCL-XML=$(PACKAGEDIR_FCL-XML)
+endif
+ifdef CHECKDEPEND
+$(PACKAGEDIR_FCL-XML)/$(FPCMADE):
+	$(MAKE) -C $(PACKAGEDIR_FCL-XML) $(FPCMADE)
+override ALLDEPENDENCIES+=$(PACKAGEDIR_FCL-XML)/$(FPCMADE)
+endif
+else
+PACKAGEDIR_FCL-XML=
+UNITDIR_FCL-XML:=$(subst /Package.fpc,,$(strip $(wildcard $(addsuffix /fcl-xml/Package.fpc,$(UNITSDIR)))))
+ifneq ($(UNITDIR_FCL-XML),)
+UNITDIR_FCL-XML:=$(firstword $(UNITDIR_FCL-XML))
+else
+UNITDIR_FCL-XML=
+endif
+endif
+ifdef UNITDIR_FCL-XML
+override COMPILER_UNITDIR+=$(UNITDIR_FCL-XML)
+endif
+endif
+ifdef REQUIRE_PACKAGES_FCL-JSON
+PACKAGEDIR_FCL-JSON:=$(firstword $(subst /Makefile.fpc,,$(strip $(wildcard $(addsuffix /fcl-json/Makefile.fpc,$(PACKAGESDIR))))))
+ifneq ($(PACKAGEDIR_FCL-JSON),)
+ifneq ($(wildcard $(PACKAGEDIR_FCL-JSON)/units/$(TARGETSUFFIX)),)
+UNITDIR_FCL-JSON=$(PACKAGEDIR_FCL-JSON)/units/$(TARGETSUFFIX)
+else
+UNITDIR_FCL-JSON=$(PACKAGEDIR_FCL-JSON)
+endif
+ifdef CHECKDEPEND
+$(PACKAGEDIR_FCL-JSON)/$(FPCMADE):
+	$(MAKE) -C $(PACKAGEDIR_FCL-JSON) $(FPCMADE)
+override ALLDEPENDENCIES+=$(PACKAGEDIR_FCL-JSON)/$(FPCMADE)
+endif
+else
+PACKAGEDIR_FCL-JSON=
+UNITDIR_FCL-JSON:=$(subst /Package.fpc,,$(strip $(wildcard $(addsuffix /fcl-json/Package.fpc,$(UNITSDIR)))))
+ifneq ($(UNITDIR_FCL-JSON),)
+UNITDIR_FCL-JSON:=$(firstword $(UNITDIR_FCL-JSON))
+else
+UNITDIR_FCL-JSON=
+endif
+endif
+ifdef UNITDIR_FCL-JSON
+override COMPILER_UNITDIR+=$(UNITDIR_FCL-JSON)
+endif
+endif
+ifdef REQUIRE_PACKAGES_UNIVINT
+PACKAGEDIR_UNIVINT:=$(firstword $(subst /Makefile.fpc,,$(strip $(wildcard $(addsuffix /univint/Makefile.fpc,$(PACKAGESDIR))))))
+ifneq ($(PACKAGEDIR_UNIVINT),)
+ifneq ($(wildcard $(PACKAGEDIR_UNIVINT)/units/$(TARGETSUFFIX)),)
+UNITDIR_UNIVINT=$(PACKAGEDIR_UNIVINT)/units/$(TARGETSUFFIX)
+else
+UNITDIR_UNIVINT=$(PACKAGEDIR_UNIVINT)
+endif
+ifdef CHECKDEPEND
+$(PACKAGEDIR_UNIVINT)/$(FPCMADE):
+	$(MAKE) -C $(PACKAGEDIR_UNIVINT) $(FPCMADE)
+override ALLDEPENDENCIES+=$(PACKAGEDIR_UNIVINT)/$(FPCMADE)
+endif
+else
+PACKAGEDIR_UNIVINT=
+UNITDIR_UNIVINT:=$(subst /Package.fpc,,$(strip $(wildcard $(addsuffix /univint/Package.fpc,$(UNITSDIR)))))
+ifneq ($(UNITDIR_UNIVINT),)
+UNITDIR_UNIVINT:=$(firstword $(UNITDIR_UNIVINT))
+else
+UNITDIR_UNIVINT=
+endif
+endif
+ifdef UNITDIR_UNIVINT
+override COMPILER_UNITDIR+=$(UNITDIR_UNIVINT)
+endif
+endif
+ifndef NOCPUDEF
+override FPCOPTDEF=$(ARCH)
+endif
+ifneq ($(OS_TARGET),$(OS_SOURCE))
+override FPCOPT+=-T$(OS_TARGET)
+endif
+ifneq ($(CPU_TARGET),$(CPU_SOURCE))
+override FPCOPT+=-P$(ARCH)
+endif
+ifeq ($(OS_SOURCE),openbsd)
+override FPCOPT+=-FD$(NEW_BINUTILS_PATH)
+endif
+ifndef CROSSBOOTSTRAP
+ifneq ($(BINUTILSPREFIX),)
+override FPCOPT+=-XP$(BINUTILSPREFIX)
+endif
+ifneq ($(BINUTILSPREFIX),)
+override FPCOPT+=-Xr$(RLINKPATH)
+endif
+endif
+ifdef UNITDIR
+override FPCOPT+=$(addprefix -Fu,$(UNITDIR))
+endif
+ifdef LIBDIR
+override FPCOPT+=$(addprefix -Fl,$(LIBDIR))
+endif
+ifdef OBJDIR
+override FPCOPT+=$(addprefix -Fo,$(OBJDIR))
+endif
+ifdef INCDIR
+override FPCOPT+=$(addprefix -Fi,$(INCDIR))
+endif
+ifdef LINKSMART
+override FPCOPT+=-XX
+endif
+ifdef CREATESMART
+override FPCOPT+=-CX
+endif
+ifdef DEBUG
+override FPCOPT+=-gl
+override FPCOPTDEF+=DEBUG
+endif
+ifdef RELEASE
+ifneq ($(findstring 2.0.,$(FPC_VERSION)),)
+ifeq ($(CPU_TARGET),i386)
+FPCCPUOPT:=-OG2p3
+endif
+ifeq ($(CPU_TARGET),powerpc)
+FPCCPUOPT:=-O1r
+endif
+else
+FPCCPUOPT:=-O2
+endif
+override FPCOPT+=-Ur -Xs $(FPCCPUOPT) -n
+override FPCOPTDEF+=RELEASE
+endif
+ifdef STRIP
+override FPCOPT+=-Xs
+endif
+ifdef OPTIMIZE
+override FPCOPT+=-O2
+endif
+ifdef VERBOSE
+override FPCOPT+=-vwni
+endif
+ifdef COMPILER_OPTIONS
+override FPCOPT+=$(COMPILER_OPTIONS)
+endif
+ifdef COMPILER_UNITDIR
+override FPCOPT+=$(addprefix -Fu,$(COMPILER_UNITDIR))
+endif
+ifdef COMPILER_LIBRARYDIR
+override FPCOPT+=$(addprefix -Fl,$(COMPILER_LIBRARYDIR))
+endif
+ifdef COMPILER_OBJECTDIR
+override FPCOPT+=$(addprefix -Fo,$(COMPILER_OBJECTDIR))
+endif
+ifdef COMPILER_INCLUDEDIR
+override FPCOPT+=$(addprefix -Fi,$(COMPILER_INCLUDEDIR))
+endif
+ifdef CROSSBINDIR
+override FPCOPT+=-FD$(CROSSBINDIR)
+endif
+ifdef COMPILER_TARGETDIR
+override FPCOPT+=-FE$(COMPILER_TARGETDIR)
+ifeq ($(COMPILER_TARGETDIR),.)
+override TARGETDIRPREFIX=
+else
+override TARGETDIRPREFIX=$(COMPILER_TARGETDIR)/
+endif
+endif
+ifdef COMPILER_UNITTARGETDIR
+override FPCOPT+=-FU$(COMPILER_UNITTARGETDIR)
+ifeq ($(COMPILER_UNITTARGETDIR),.)
+override UNITTARGETDIRPREFIX=
+else
+override UNITTARGETDIRPREFIX=$(COMPILER_UNITTARGETDIR)/
+endif
+else
+ifdef COMPILER_TARGETDIR
+override COMPILER_UNITTARGETDIR=$(COMPILER_TARGETDIR)
+override UNITTARGETDIRPREFIX=$(TARGETDIRPREFIX)
+endif
+endif
+ifdef CREATESHARED
+override FPCOPT+=-Cg
+ifeq ($(CPU_TARGET),i386)
+override FPCOPT+=-Aas
+endif
+endif
+ifeq ($(findstring 2.0.,$(FPC_VERSION)),)
+ifeq ($(OS_TARGET),linux)
+ifeq ($(CPU_TARGET),x86_64)
+override FPCOPT+=-Cg
+endif
+endif
+endif
+ifdef LINKSHARED
+endif
+ifdef GCCLIBDIR
+override FPCOPT+=-Fl$(GCCLIBDIR)
+endif
+ifdef OTHERLIBDIR
+override FPCOPT+=$(addprefix -Fl,$(OTHERLIBDIR))
+endif
+ifdef OPT
+override FPCOPT+=$(OPT)
+endif
+ifdef FPCOPTDEF
+override FPCOPT+=$(addprefix -d,$(FPCOPTDEF))
+endif
+ifdef CFGFILE
+override FPCOPT+=@$(CFGFILE)
+endif
+ifdef USEENV
+override FPCEXTCMD:=$(FPCOPT)
+override FPCOPT:=!FPCEXTCMD
+export FPCEXTCMD
+endif
+override AFULL_TARGET=$(CPU_TARGET)-$(OS_TARGET)
+override AFULL_SOURCE=$(CPU_SOURCE)-$(OS_SOURCE)
+ifneq ($(AFULL_TARGET),$(AFULL_SOURCE))
+override ACROSSCOMPILE=1
+endif
+ifdef ACROSSCOMPILE
+override FPCOPT+=$(CROSSOPT)
+endif
+override COMPILER:=$(FPC) $(FPCOPT)
+ifeq (,$(findstring -s ,$(COMPILER)))
+EXECPPAS=
+else
+ifeq ($(FULL_SOURCE),$(FULL_TARGET))
+ifdef RUNBATCH
+EXECPPAS:=@$(RUNBATCH) $(PPAS)
+else
+EXECPPAS:=@$(PPAS)
+endif
+endif
+endif
+.PHONY: fpc_units
+ifneq ($(TARGET_UNITS)$(TARGET_IMPLICITUNITS),)
+override ALLTARGET+=fpc_units
+override UNITPPUFILES=$(addsuffix $(PPUEXT),$(TARGET_UNITS))
+override IMPLICITUNITPPUFILES=$(addsuffix $(PPUEXT),$(TARGET_IMPLICITUNITS))
+override INSTALLPPUFILES+=$(UNITPPUFILES) $(IMPLICITUNITPPUFILES)
+override CLEANPPUFILES+=$(UNITPPUFILES) $(IMPLICITUNITPPUFILES)
+endif
+fpc_units: $(COMPILER_UNITTARGETDIR) $(UNITPPUFILES)
+ifdef TARGET_RSTS
+override RSTFILES=$(addsuffix $(RSTEXT),$(TARGET_RSTS))
+override CLEANRSTFILES+=$(RSTFILES)
+endif
+.PHONY: fpc_all fpc_smart fpc_debug fpc_release fpc_shared
+$(FPCMADE): $(ALLDEPENDENCIES) $(ALLTARGET)
+	@$(ECHOREDIR) Compiled > $(FPCMADE)
+fpc_all: $(FPCMADE)
+fpc_smart:
+	$(MAKE) all LINKSMART=1 CREATESMART=1
+fpc_debug:
+	$(MAKE) all DEBUG=1
+fpc_release:
+	$(MAKE) all RELEASE=1
+.SUFFIXES: $(EXEEXT) $(PPUEXT) $(OEXT) .pas .lpr .dpr .pp .rc .res
+$(COMPILER_UNITTARGETDIR):
+	$(MKDIRTREE) $(COMPILER_UNITTARGETDIR)
+$(COMPILER_TARGETDIR):
+	$(MKDIRTREE) $(COMPILER_TARGETDIR)
+%$(PPUEXT): %.pp
+	$(COMPILER) $<
+	$(EXECPPAS)
+%$(PPUEXT): %.pas
+	$(COMPILER) $<
+	$(EXECPPAS)
+%$(EXEEXT): %.pp
+	$(COMPILER) $<
+	$(EXECPPAS)
+%$(EXEEXT): %.pas
+	$(COMPILER) $<
+	$(EXECPPAS)
+%$(EXEEXT): %.lpr
+	$(COMPILER) $<
+	$(EXECPPAS)
+%$(EXEEXT): %.dpr
+	$(COMPILER) $<
+	$(EXECPPAS)
+%.res: %.rc
+	windres -i $< -o $@
+vpath %.pp $(COMPILER_SOURCEDIR) $(COMPILER_INCLUDEDIR)
+vpath %.pas $(COMPILER_SOURCEDIR) $(COMPILER_INCLUDEDIR)
+vpath %.lpr $(COMPILER_SOURCEDIR) $(COMPILER_INCLUDEDIR)
+vpath %.dpr $(COMPILER_SOURCEDIR) $(COMPILER_INCLUDEDIR)
+vpath %.inc $(COMPILER_INCLUDEDIR)
+vpath %$(OEXT) $(COMPILER_UNITTARGETDIR)
+vpath %$(PPUEXT) $(COMPILER_UNITTARGETDIR)
+.PHONY: fpc_shared
+override INSTALLTARGET+=fpc_shared_install
+ifndef SHARED_LIBVERSION
+SHARED_LIBVERSION=$(FPC_VERSION)
+endif
+ifndef SHARED_LIBNAME
+SHARED_LIBNAME=$(PACKAGE_NAME)
+endif
+ifndef SHARED_FULLNAME
+SHARED_FULLNAME=$(SHAREDLIBPREFIX)$(SHARED_LIBNAME)-$(SHARED_LIBVERSION)$(SHAREDLIBEXT)
+endif
+ifndef SHARED_LIBUNITS
+SHARED_LIBUNITS:=$(TARGET_UNITS) $(TARGET_IMPLICITUNITS)
+override SHARED_LIBUNITS:=$(filter-out $(INSTALL_BUILDUNIT),$(SHARED_LIBUNITS))
+endif
+fpc_shared:
+ifdef HASSHAREDLIB
+	$(MAKE) all CREATESHARED=1 LINKSHARED=1 CREATESMART=1
+ifneq ($(SHARED_BUILD),n)
+	$(PPUMOVE) -q $(SHARED_LIBUNITS) -i$(COMPILER_UNITTARGETDIR) -o$(SHARED_FULLNAME) -d$(COMPILER_UNITTARGETDIR)
+endif
+else
+	@$(ECHO) Shared Libraries not supported
+endif
+fpc_shared_install:
+ifneq ($(SHARED_BUILD),n)
+ifneq ($(SHARED_LIBUNITS),)
+ifneq ($(wildcard $(COMPILER_UNITTARGETDIR)/$(SHARED_FULLNAME)),)
+	$(INSTALL) $(COMPILER_UNITTARGETDIR)/$(SHARED_FULLNAME) $(INSTALL_SHAREDDIR)
+endif
+endif
+endif
+.PHONY: fpc_install fpc_sourceinstall fpc_exampleinstall
+ifdef INSTALL_UNITS
+override INSTALLPPUFILES+=$(addsuffix $(PPUEXT),$(INSTALL_UNITS))
+endif
+ifdef INSTALL_BUILDUNIT
+override INSTALLPPUFILES:=$(filter-out $(INSTALL_BUILDUNIT)$(PPUEXT),$(INSTALLPPUFILES))
+endif
+ifdef INSTALLPPUFILES
+override INSTALLPPULINKFILES:=$(subst $(PPUEXT),$(OEXT),$(INSTALLPPUFILES)) $(addprefix $(STATICLIBPREFIX),$(subst $(PPUEXT),$(STATICLIBEXT),$(INSTALLPPUFILES))) $(addprefix $(IMPORTLIBPREFIX),$(subst $(PPUEXT),$(STATICLIBEXT),$(INSTALLPPUFILES)))
+ifneq ($(UNITTARGETDIRPREFIX),)
+override INSTALLPPUFILES:=$(addprefix $(UNITTARGETDIRPREFIX),$(notdir $(INSTALLPPUFILES)))
+override INSTALLPPULINKFILES:=$(wildcard $(addprefix $(UNITTARGETDIRPREFIX),$(notdir $(INSTALLPPULINKFILES))))
+endif
+override INSTALL_CREATEPACKAGEFPC=1
+endif
+ifdef INSTALLEXEFILES
+ifneq ($(TARGETDIRPREFIX),)
+override INSTALLEXEFILES:=$(addprefix $(TARGETDIRPREFIX),$(notdir $(INSTALLEXEFILES)))
+endif
+endif
+fpc_install: all $(INSTALLTARGET)
+ifdef INSTALLEXEFILES
+	$(MKDIR) $(INSTALL_BINDIR)
+ifdef UPXPROG
+	-$(UPXPROG) $(INSTALLEXEFILES)
+endif
+	$(INSTALLEXE) $(INSTALLEXEFILES) $(INSTALL_BINDIR)
+endif
+ifdef INSTALL_CREATEPACKAGEFPC
+ifdef FPCMAKE
+ifdef PACKAGE_VERSION
+ifneq ($(wildcard Makefile.fpc),)
+	$(FPCMAKE) -p -T$(CPU_TARGET)-$(OS_TARGET) Makefile.fpc
+	$(MKDIR) $(INSTALL_UNITDIR)
+	$(INSTALL) Package.fpc $(INSTALL_UNITDIR)
+endif
+endif
+endif
+endif
+ifdef INSTALLPPUFILES
+	$(MKDIR) $(INSTALL_UNITDIR)
+	$(INSTALL) $(INSTALLPPUFILES) $(INSTALL_UNITDIR)
+ifneq ($(INSTALLPPULINKFILES),)
+	$(INSTALL) $(INSTALLPPULINKFILES) $(INSTALL_UNITDIR)
+endif
+ifneq ($(wildcard $(LIB_FULLNAME)),)
+	$(MKDIR) $(INSTALL_LIBDIR)
+	$(INSTALL) $(LIB_FULLNAME) $(INSTALL_LIBDIR)
+ifdef inUnix
+	ln -sf $(LIB_FULLNAME) $(INSTALL_LIBDIR)/$(LIB_NAME)
+endif
+endif
+endif
+ifdef INSTALL_FILES
+	$(MKDIR) $(INSTALL_DATADIR)
+	$(INSTALL) $(INSTALL_FILES) $(INSTALL_DATADIR)
+endif
+fpc_sourceinstall: distclean
+	$(MKDIR) $(INSTALL_SOURCEDIR)
+	$(COPYTREE) $(BASEDIR)/* $(INSTALL_SOURCEDIR)
+fpc_exampleinstall: $(addsuffix _distclean,$(TARGET_EXAMPLEDIRS))
+ifdef HASEXAMPLES
+	$(MKDIR) $(INSTALL_EXAMPLEDIR)
+endif
+ifdef EXAMPLESOURCEFILES
+	$(COPY) $(EXAMPLESOURCEFILES) $(INSTALL_EXAMPLEDIR)
+endif
+ifdef TARGET_EXAMPLEDIRS
+	$(COPYTREE) $(addsuffix /*,$(TARGET_EXAMPLEDIRS)) $(INSTALL_EXAMPLEDIR)
+endif
+.PHONY: fpc_clean fpc_cleanall fpc_distclean
+ifdef EXEFILES
+override CLEANEXEFILES:=$(addprefix $(TARGETDIRPREFIX),$(CLEANEXEFILES))
+endif
+ifdef CLEAN_UNITS
+override CLEANPPUFILES+=$(addsuffix $(PPUEXT),$(CLEAN_UNITS))
+endif
+ifdef CLEANPPUFILES
+override CLEANPPULINKFILES:=$(subst $(PPUEXT),$(OEXT),$(CLEANPPUFILES)) $(addprefix $(STATICLIBPREFIX),$(subst $(PPUEXT),$(STATICLIBEXT),$(CLEANPPUFILES))) $(addprefix $(IMPORTLIBPREFIX),$(subst $(PPUEXT),$(STATICLIBEXT),$(CLEANPPUFILES)))
+ifdef DEBUGSYMEXT
+override CLEANPPULINKFILES+=$(subst $(PPUEXT),$(DEBUGSYMEXT),$(CLEANPPUFILES))
+endif
+override CLEANPPUFILES:=$(addprefix $(UNITTARGETDIRPREFIX),$(CLEANPPUFILES))
+override CLEANPPULINKFILES:=$(wildcard $(addprefix $(UNITTARGETDIRPREFIX),$(CLEANPPULINKFILES)))
+endif
+fpc_clean: $(CLEANTARGET)
+ifdef CLEANEXEFILES
+	-$(DEL) $(CLEANEXEFILES)
+endif
+ifdef CLEANPPUFILES
+	-$(DEL) $(CLEANPPUFILES)
+endif
+ifneq ($(CLEANPPULINKFILES),)
+	-$(DEL) $(CLEANPPULINKFILES)
+endif
+ifdef CLEANRSTFILES
+	-$(DEL) $(addprefix $(UNITTARGETDIRPREFIX),$(CLEANRSTFILES))
+endif
+ifdef CLEAN_FILES
+	-$(DEL) $(CLEAN_FILES)
+endif
+ifdef LIB_NAME
+	-$(DEL) $(LIB_NAME) $(LIB_FULLNAME)
+endif
+	-$(DEL) $(FPCMADE) Package.fpc $(PPAS) script.res link.res $(FPCEXTFILE) $(REDIRFILE)
+	-$(DEL) *$(ASMEXT) *_ppas$(BATCHEXT)
+fpc_cleanall: $(CLEANTARGET)
+ifdef CLEANEXEFILES
+	-$(DEL) $(CLEANEXEFILES)
+endif
+ifdef COMPILER_UNITTARGETDIR
+ifdef CLEANPPUFILES
+	-$(DEL) $(CLEANPPUFILES)
+endif
+ifneq ($(CLEANPPULINKFILES),)
+	-$(DEL) $(CLEANPPULINKFILES)
+endif
+ifdef CLEANRSTFILES
+	-$(DEL) $(addprefix $(UNITTARGETDIRPREFIX),$(CLEANRSTFILES))
+endif
+endif
+	-$(DELTREE) units
+	-$(DEL) *$(OEXT) *$(PPUEXT) *$(RSTEXT) *$(ASMEXT) *$(STATICLIBEXT) *$(SHAREDLIBEXT) *$(PPLEXT)
+ifneq ($(PPUEXT),.ppu)
+	-$(DEL) *.o *.ppu *.a
+endif
+	-$(DELTREE) *$(SMARTEXT)
+	-$(DEL) fpcmade.* Package.fpc $(PPAS) script.res link.res $(FPCEXTFILE) $(REDIRFILE)
+	-$(DEL) *_ppas$(BATCHEXT)
+ifdef AOUTEXT
+	-$(DEL) *$(AOUTEXT)
+endif
+ifdef DEBUGSYMEXT
+	-$(DEL) *$(DEBUGSYMEXT)
+endif
+fpc_distclean: cleanall
+.PHONY: fpc_baseinfo
+override INFORULES+=fpc_baseinfo
+fpc_baseinfo:
+	@$(ECHO)
+	@$(ECHO)  == Package info ==
+	@$(ECHO)  Package Name..... $(PACKAGE_NAME)
+	@$(ECHO)  Package Version.. $(PACKAGE_VERSION)
+	@$(ECHO)
+	@$(ECHO)  == Configuration info ==
+	@$(ECHO)
+	@$(ECHO)  FPC.......... $(FPC)
+	@$(ECHO)  FPC Version.. $(FPC_VERSION)
+	@$(ECHO)  Source CPU... $(CPU_SOURCE)
+	@$(ECHO)  Target CPU... $(CPU_TARGET)
+	@$(ECHO)  Source OS.... $(OS_SOURCE)
+	@$(ECHO)  Target OS.... $(OS_TARGET)
+	@$(ECHO)  Full Source.. $(FULL_SOURCE)
+	@$(ECHO)  Full Target.. $(FULL_TARGET)
+	@$(ECHO)  SourceSuffix. $(SOURCESUFFIX)
+	@$(ECHO)  TargetSuffix. $(TARGETSUFFIX)
+	@$(ECHO)
+	@$(ECHO)  == Directory info ==
+	@$(ECHO)
+	@$(ECHO)  Required pkgs... $(REQUIRE_PACKAGES)
+	@$(ECHO)
+	@$(ECHO)  Basedir......... $(BASEDIR)
+	@$(ECHO)  FPCDir.......... $(FPCDIR)
+	@$(ECHO)  CrossBinDir..... $(CROSSBINDIR)
+	@$(ECHO)  UnitsDir........ $(UNITSDIR)
+	@$(ECHO)  PackagesDir..... $(PACKAGESDIR)
+	@$(ECHO)
+	@$(ECHO)  GCC library..... $(GCCLIBDIR)
+	@$(ECHO)  Other library... $(OTHERLIBDIR)
+	@$(ECHO)
+	@$(ECHO)  == Tools info ==
+	@$(ECHO)
+	@$(ECHO)  As........ $(AS)
+	@$(ECHO)  Ld........ $(LD)
+	@$(ECHO)  Ar........ $(AR)
+	@$(ECHO)  Rc........ $(RC)
+	@$(ECHO)
+	@$(ECHO)  Mv........ $(MVPROG)
+	@$(ECHO)  Cp........ $(CPPROG)
+	@$(ECHO)  Rm........ $(RMPROG)
+	@$(ECHO)  GInstall.. $(GINSTALL)
+	@$(ECHO)  Echo...... $(ECHO)
+	@$(ECHO)  Shell..... $(SHELL)
+	@$(ECHO)  Date...... $(DATE)
+	@$(ECHO)  FPCMake... $(FPCMAKE)
+	@$(ECHO)  PPUMove... $(PPUMOVE)
+	@$(ECHO)  Upx....... $(UPXPROG)
+	@$(ECHO)  Zip....... $(ZIPPROG)
+	@$(ECHO)
+	@$(ECHO)  == Object info ==
+	@$(ECHO)
+	@$(ECHO)  Target Loaders........ $(TARGET_LOADERS)
+	@$(ECHO)  Target Units.......... $(TARGET_UNITS)
+	@$(ECHO)  Target Implicit Units. $(TARGET_IMPLICITUNITS)
+	@$(ECHO)  Target Programs....... $(TARGET_PROGRAMS)
+	@$(ECHO)  Target Dirs........... $(TARGET_DIRS)
+	@$(ECHO)  Target Examples....... $(TARGET_EXAMPLES)
+	@$(ECHO)  Target ExampleDirs.... $(TARGET_EXAMPLEDIRS)
+	@$(ECHO)
+	@$(ECHO)  Clean Units......... $(CLEAN_UNITS)
+	@$(ECHO)  Clean Files......... $(CLEAN_FILES)
+	@$(ECHO)
+	@$(ECHO)  Install Units....... $(INSTALL_UNITS)
+	@$(ECHO)  Install Files....... $(INSTALL_FILES)
+	@$(ECHO)
+	@$(ECHO)  == Install info ==
+	@$(ECHO)
+	@$(ECHO)  DateStr.............. $(DATESTR)
+	@$(ECHO)  ZipName.............. $(ZIPNAME)
+	@$(ECHO)  ZipPrefix............ $(ZIPPREFIX)
+	@$(ECHO)  ZipCrossPrefix....... $(ZIPCROSSPREFIX)
+	@$(ECHO)  ZipSuffix............ $(ZIPSUFFIX)
+	@$(ECHO)  FullZipName.......... $(FULLZIPNAME)
+	@$(ECHO)  Install FPC Package.. $(INSTALL_FPCPACKAGE)
+	@$(ECHO)
+	@$(ECHO)  Install base dir..... $(INSTALL_BASEDIR)
+	@$(ECHO)  Install binary dir... $(INSTALL_BINDIR)
+	@$(ECHO)  Install library dir.. $(INSTALL_LIBDIR)
+	@$(ECHO)  Install units dir.... $(INSTALL_UNITDIR)
+	@$(ECHO)  Install source dir... $(INSTALL_SOURCEDIR)
+	@$(ECHO)  Install doc dir...... $(INSTALL_DOCDIR)
+	@$(ECHO)  Install example dir.. $(INSTALL_EXAMPLEDIR)
+	@$(ECHO)  Install data dir..... $(INSTALL_DATADIR)
+	@$(ECHO)
+	@$(ECHO)  Dist destination dir. $(DIST_DESTDIR)
+	@$(ECHO)  Dist zip name........ $(DIST_ZIPNAME)
+	@$(ECHO)
+.PHONY: fpc_info
+fpc_info: $(INFORULES)
+.PHONY: fpc_makefile fpc_makefiles fpc_makefile_sub1 fpc_makefile_sub2 \
+	fpc_makefile_dirs
+fpc_makefile:
+	$(FPCMAKE) -w -T$(OS_TARGET) Makefile.fpc
+fpc_makefile_sub1:
+ifdef TARGET_DIRS
+	$(FPCMAKE) -w -T$(OS_TARGET) $(addsuffix /Makefile.fpc,$(TARGET_DIRS))
+endif
+ifdef TARGET_EXAMPLEDIRS
+	$(FPCMAKE) -w -T$(OS_TARGET) $(addsuffix /Makefile.fpc,$(TARGET_EXAMPLEDIRS))
+endif
+fpc_makefile_sub2: $(addsuffix _makefile_dirs,$(TARGET_DIRS) $(TARGET_EXAMPLEDIRS))
+fpc_makefile_dirs: fpc_makefile_sub1 fpc_makefile_sub2
+fpc_makefiles: fpc_makefile fpc_makefile_dirs
+all: fpc_all
+debug: fpc_debug
+smart: fpc_smart
+release: fpc_release
+units: fpc_units
+examples:
+shared: fpc_shared
+install: fpc_install
+sourceinstall: fpc_sourceinstall
+exampleinstall: fpc_exampleinstall
+distinstall:
+zipinstall:
+zipsourceinstall:
+zipexampleinstall:
+zipdistinstall:
+clean: fpc_clean
+distclean: fpc_distclean
+cleanall: fpc_cleanall
+info: fpc_info
+makefiles: fpc_makefiles
+.PHONY: all debug smart release units examples shared install sourceinstall exampleinstall distinstall zipinstall zipsourceinstall zipexampleinstall zipdistinstall clean distclean cleanall info makefiles
+ifneq ($(wildcard fpcmake.loc),)
+include fpcmake.loc
+endif
+.NOTPARALLEL:

+ 26 - 0
packages/fcl-web/src/jsonrpc/Makefile.fpc

@@ -0,0 +1,26 @@
+#
+#   Makefile.fpc for FCL Web components
+#
+
+[package]
+main=fcl-web
+version=2.5.1
+
+[target]
+units=fpjsonrpc  webjsonrpc fpextdirect
+
+[require]
+packages=fcl-base fcl-xml fcl-json
+
+[compiler]
+options=-S2h
+
+[install]
+fpcpackage=y
+
+[default]
+fpcdir=../../../..
+
+
+[rules]
+.NOTPARALLEL:

+ 430 - 0
packages/fcl-web/src/jsonrpc/fpextdirect.pp

@@ -0,0 +1,430 @@
+unit fpextdirect;
+
+{$mode objfpc}{$H+}
+{ $define extdebug}
+
+interface
+
+uses
+  Classes, SysUtils, fpjson, fpjsonrpc, webjsonrpc, httpdefs,websession;
+
+Const
+  DefaultExtDirectOptions = DefaultDispatchOptions + [jdoRequireClass];
+
+Type
+  { TCustomExtDirectDispatcher }
+
+  TCustomExtDirectDispatcher = Class(TCustomJSONRPCDispatcher)
+  private
+    FAPIType: String;
+    FNameSpace: String;
+    FURL: String;
+    function GetNameSpace: String;
+    function isNameSpaceStored: boolean;
+  Protected
+    function FormatResult(const AClassName, AMethodName: TJSONStringType;
+      const Params, ID, Return: TJSONData): TJSONData; override;
+    // 'tid'
+    Class Function TransactionProperty : String; override;
+    // 'method'
+    Class Function MethodProperty : String; override;
+    // 'action'
+    Class Function ClassNameProperty : String; override;
+    // 'data'
+    Class Function ParamsProperty : String; override;
+    // Add session support
+    Function FindHandler(Const AClassName,AMethodName : TJSONStringType;AContext : TJSONRPCCallContext; Out FreeObject : TComponent) : TCustomJSONRPCHandler; override;
+    // Create API
+    Function DoAPI : TJSONData; virtual;
+    // Namespace for API description. Must be set. Default 'FPWeb'
+    Property NameSpace : String Read GetNameSpace Write FNameSpace Stored isNameSpaceStored;
+    // URL property for router. Must be set
+    Property URL : String Read FURL Write FURL;
+    // "type". By default: 'remoting'
+    Property APIType : String Read FAPIType Write FAPIType;
+  Public
+    // Override to set additional opions.
+    Constructor Create(AOwner : TComponent); override;
+    // Return API description object
+    Function API: TJSONData;
+    // Return API Description including namespace, as a string
+    Function APIAsString : String;
+  end;
+
+  { TExtDirectDispatcher }
+
+  TExtDirectDispatcher = Class(TCustomExtDirectDispatcher)
+  Published
+    Property NameSpace;
+    Property URL;
+    Property APIType;
+    Property OnStartBatch;
+    Property OnDispatchRequest;
+    Property OnFindHandler;
+    Property OnEndBatch;
+    Property Options;
+  end;
+
+  { TCustomExtDirectContentProducer }
+
+  TCustomExtDirectContentProducer = Class(TCustomJSONRPCContentProducer)
+  Protected
+    Function GetIDProperty : String; override;
+    Procedure DoGetContent(ARequest : TRequest; Content : TStream; Var Handled : Boolean); override;
+  end;
+
+  { TExtDirectContentProducer }
+
+  TExtDirectContentProducer = Class(TCustomExtDirectContentProducer)
+  private
+    FDispatcher: TCustomExtDirectDispatcher;
+    procedure SetDispatcher(const AValue: TCustomExtDirectDispatcher);
+  Protected
+    Function GetDispatcher : TCustomJSONRPCDispatcher; override;
+    procedure Notification(AComponent: TComponent; Operation: TOperation);override;
+  Published
+    Property Dispatcher :  TCustomExtDirectDispatcher Read FDispatcher Write SetDispatcher;
+  end;
+
+  { TCustomExtDirectModule }
+
+  TCustomExtDirectModule = Class(TJSONRPCDispatchModule)
+  private
+    FAPIPath: String;
+    FDispatcher: TCustomExtDirectDispatcher;
+    FOptions: TJSONRPCDispatchOptions;
+    FRequest: TRequest;
+    FResponse: TResponse;
+    FRouterPath: String;
+    procedure SetDispatcher(const AValue: TCustomExtDirectDispatcher);
+  Protected
+    // Create API
+    procedure CreateAPI(ADispatcher: TCustomExtDirectDispatcher; ARequest: TRequest; AResponse: TResponse); virtual;
+    Function CreateDispatcher : TCustomExtDirectDispatcher; virtual;
+    procedure Notification(AComponent: TComponent; Operation: TOperation);override;
+    // Set to a custom dispatcher. If not set, one is created (and kept for all subsequent requests)
+    Property Dispatcher :  TCustomExtDirectDispatcher Read FDispatcher Write SetDispatcher;
+    // Options to use when creating a dispatcher.
+    Property DispatchOptions : TJSONRPCDispatchOptions Read FOptions Write FOptions default DefaultDispatchOptions;
+    // API path/action. Append to BaseURL to get API. Default 'API'
+    Property APIPath : String Read FAPIPath Write FAPIPath;
+    // Router path/action. Append to baseURL to get router. Default 'router'
+    Property RouterPath : String Read FRouterPath Write FRouterPath;
+  Public
+    Constructor CreateNew(AOwner : TComponent; CreateMode : Integer); override;
+    Procedure HandleRequest(ARequest : TRequest; AResponse : TResponse); override;
+    // Access to request
+    Property Request: TRequest Read FRequest;
+    // Access to response
+    Property Response: TResponse Read FResponse;
+  end;
+
+  TExtDirectModule = Class(TCustomExtDirectModule)
+  Published
+    Property Dispatcher;
+    Property DispatchOptions;
+    Property APIPath;
+    Property RouterPath;
+  end;
+
+implementation
+
+{$ifdef extdebug}
+uses dbugintf;
+{$endif}
+
+Resourcestring
+  SErrInvalidPath = 'Invalid path';
+
+{ TCustomExtDirectDispatcher }
+Const
+  DefaultNameSpace = 'FPWeb';
+
+function TCustomExtDirectDispatcher.GetNameSpace: String;
+begin
+  Result:=FNameSpace;
+  If (Result='') then
+    Result:=DefaultNameSpace
+end;
+
+function TCustomExtDirectDispatcher.isNameSpaceStored: boolean;
+begin
+  Result:=NameSpace<>DefaultNameSpace;
+end;
+
+function TCustomExtDirectDispatcher.FormatResult(Const AClassName, AMethodName: TJSONStringType;
+Const Params,ID, Return : TJSONData) : TJSONData;
+
+begin
+  Result:=Inherited FormatResult(AClassName,AMethodName,Params,ID,Return);
+  TJSONObject(Result).Add('type','rpc');
+  TJSONObject(Result).Add('action',AClassName);
+  TJSONObject(Result).Add('method',AMethodName);
+end;
+
+class function TCustomExtDirectDispatcher.TransactionProperty: String;
+begin
+  Result:='tid';
+end;
+
+class function TCustomExtDirectDispatcher.MethodProperty: String;
+begin
+  Result:='method';
+end;
+
+class function TCustomExtDirectDispatcher.ClassNameProperty: String;
+begin
+  Result:='action';
+end;
+
+class function TCustomExtDirectDispatcher.ParamsProperty: String;
+begin
+  Result:='data';
+end;
+
+function TCustomExtDirectDispatcher.FindHandler(const AClassName,
+  AMethodName: TJSONStringType; AContext: TJSONRPCCallContext; out
+  FreeObject: TComponent): TCustomJSONRPCHandler;
+begin
+  {$ifdef extdebug}SendDebugFmt('Searching for %s %s',[AClassName,AMethodName]);{$endif}
+  Result:=inherited FindHandler(AClassName, AMethodName, AContext, FreeObject);
+  If (AContext is TJSONRPCSessionContext) and (FreeObject is TCustomJSONRPCModule) then
+    TCustomJSONRPCModule(FreeObject).Session:=TJSONRPCSessionContext(AContext).Session;
+  {$ifdef extdebug}SendDebugFmt('Done with searching for %s %s : %d',[AClassName,AMethodName,Ord(Assigned(Result))]);{$endif}
+end;
+
+function TCustomExtDirectDispatcher.DoAPI: TJSONData;
+
+Var
+  A,D : TJSONObject;
+  R : TJSONArray;
+  N : TJSONStringType;
+  H : TCustomJSONRPCHandler;
+  I,J : Integer;
+  M : TCustomJSONRPCHandlerManager;
+  HD : TJSONRPCHandlerDef;
+
+begin
+  D:=TJSONObject.Create;
+  try
+    D.Add('url',URL);
+    D.Add('type',APIType);
+    A:=TJSONObject.Create;
+    D.Add('actions',A);
+    R:=Nil;
+    N:='';
+    If (jdoSearchOwner in Options) and Assigned(Owner) then
+      begin
+      for I:=Owner.ComponentCount-1 downto 0 do
+        If Owner.Components[i] is TCustomJSONRPCHandler then
+          begin
+          If (R=Nil) then
+            begin
+            N:=Owner.Name;
+            R:=TJSONArray.Create;
+            A.Add(N,R);
+            end;
+          H:=Owner.Components[i] as TCustomJSONRPCHandler;
+          R.Add(TJSONObject.Create(['name',H.Name,'len',H.ParamDefs.Count]));
+          end;
+      end;
+    If (jdoSearchRegistry in Options) then
+      begin
+      M:=JSONRPCHandlerManager;
+      For I:=M.HandlerCount-1 downto 0 do
+        begin
+        HD:=M.HandlerDefs[i];
+        If (R=Nil) or (CompareText(N,HD.HandlerClassName)<>0) then
+          begin
+          N:=HD.HandlerClassName;
+          J:=A.IndexOf(R);
+          If (J=-1) then
+            begin
+            R:=TJSONArray.Create;
+            A.Add(N,R);
+            end
+          else
+            R:=A.Items[i] as TJSONArray;
+          end;
+        R.Add(TJSONObject.Create(['name',HD.HandlerMethodName,'len',HD.ArgumentCount]));
+        end;
+      end;
+    Result:=D;
+  except
+    FreeAndNil(D);
+    Raise;
+  end;
+end;
+
+constructor TCustomExtDirectDispatcher.Create(AOwner: TComponent);
+
+Var
+  O : TJSONRPCDispatchOptions;
+
+begin
+  inherited Create(AOwner);
+  Options:=DefaultExtDirectOptions;
+  APIType:='remoting';
+end;
+
+function TCustomExtDirectDispatcher.API: TJSONData;
+begin
+  Result:=DoAPI;
+end;
+
+function TCustomExtDirectDispatcher.APIAsString: String;
+
+Var
+  A : TJSONData;
+
+begin
+  A:=API;
+  try
+    Result:=NameSpace + ' = ' + A.AsJSON + ';';
+  finally
+    A.Free;
+  end;
+end;
+
+
+{ TCustomExtDirectContentProducer }
+
+function TCustomExtDirectContentProducer.GetIDProperty: String;
+begin
+  Result:='tid';
+end;
+
+procedure TCustomExtDirectContentProducer.DoGetContent(ARequest: TRequest;
+  Content: TStream; var Handled: Boolean);
+
+Var
+  A,R: String;
+
+begin
+  A:=ARequest.GetNextPathInfo;
+  If (A<>'router') then
+    begin
+    R:=TCustomExtDirectDispatcher(GetDispatcher).APIAsString;
+    Content.WriteBuffer(R[1],Length(R));
+    Handled:=True;
+    end
+  else
+    inherited DoGetContent(ARequest, Content, Handled);
+end;
+
+{ TExtDirectContentProducer }
+
+procedure TExtDirectContentProducer.SetDispatcher(
+  const AValue: TCustomExtDirectDispatcher);
+begin
+  if FDispatcher=AValue then exit;
+  If Assigned(FDispatcher) then
+    FDispatcher.RemoveFreeNotification(Self);
+  FDispatcher:=AValue;
+  If Assigned(FDispatcher) then
+    FDispatcher.FreeNotification(Self);
+end;
+
+function TExtDirectContentProducer.GetDispatcher: TCustomJSONRPCDispatcher;
+begin
+  Result:=FDispatcher;
+end;
+
+procedure TExtDirectContentProducer.Notification(AComponent: TComponent;
+  Operation: TOperation);
+begin
+  inherited Notification(AComponent, Operation);
+  If (Operation=opRemove) and (AComponent=FDispatcher) then
+    FDispatcher:=Nil;
+end;
+
+{ TCustomExtDirectModule }
+
+procedure TCustomExtDirectModule.SetDispatcher(
+  const AValue: TCustomExtDirectDispatcher);
+begin
+  if FDispatcher=AValue then exit;
+  If Assigned(FDispatcher) then
+    FDispatcher.RemoveFreeNotification(Self);
+  FDispatcher:=AValue;
+  If Assigned(FDispatcher) then
+    FDispatcher.FreeNotification(Self);
+end;
+
+function TCustomExtDirectModule.CreateDispatcher: TCustomExtDirectDispatcher;
+
+Var
+  E : TExtDirectDispatcher;
+
+begin
+  E:=TExtDirectDispatcher.Create(Self);
+  E.Options:=DispatchOptions;
+  E.URL:=IncludeHTTPPathDelimiter(BaseURL)+RouterPath;
+  Result:=E
+end;
+
+procedure TCustomExtDirectModule.Notification(AComponent: TComponent;
+  Operation: TOperation);
+begin
+  inherited Notification(AComponent, Operation);
+  If (Operation=opRemove) and (AComponent=FDispatcher) then
+    FDispatcher:=Nil;
+end;
+
+constructor TCustomExtDirectModule.CreateNew(AOwner: TComponent;
+  CreateMode: Integer);
+begin
+  inherited CreateNew(AOwner, CreateMode);
+  FOptions:=DefaultDispatchOptions+[jdoSearchRegistry];
+  APIPath:='API';
+  RouterPath:='router'
+end;
+
+procedure TCustomExtDirectModule.CreateAPI(ADispatcher : TCustomExtDirectDispatcher; ARequest: TRequest;   AResponse: TResponse);
+
+
+begin
+  AResponse.Content:=ADispatcher.APIAsString;
+  AResponse.ContentLength:=Length(AResponse.Content);
+
+end;
+
+procedure TCustomExtDirectModule.HandleRequest(ARequest: TRequest;
+  AResponse: TResponse);
+
+Var
+  Disp : TCustomExtDirectDispatcher;
+  Req,res : TJSONData;
+  R : String;
+
+begin
+  If (Dispatcher=Nil) then
+    Dispatcher:=CreateDispatcher;
+  Disp:=Dispatcher as TCustomExtDirectDispatcher;
+  R:=ARequest.QueryFields.Values['action'];
+  If (R='') then
+    R:=ARequest.GetNextPathInfo;
+  {$ifdef extdebug}SendDebugFmt('Ext.Direct handlerequest: action is "%s"',[R]);{$endif}
+  If (CompareText(R,APIPath)=0) then
+    begin
+    CreateAPI(Disp,ARequest,AResponse);
+    AResponse.SendResponse;
+    end
+  else if (CompareText(R,RouterPath)=0) then
+    begin
+    Res:=DispatchRequest(ARequest,Disp);
+    try
+      If Assigned(Res) then
+        AResponse.Content:=Res.AsJSON;
+      AResponse.SendResponse;
+    finally
+      Res.Free;
+    end;
+    AResponse.SendResponse;
+    end
+  else
+    JSONRPCError(SErrInvalidPath);
+end;
+
+end.
+

+ 1249 - 0
packages/fcl-web/src/jsonrpc/fpjsonrpc.pp

@@ -0,0 +1,1249 @@
+unit fpjsonrpc;
+
+{$mode objfpc}{$H+}
+{$inline on}
+
+interface
+
+uses
+  Classes, SysUtils, fpjson;
+
+Type
+
+{ ---------------------------------------------------------------------
+  JSON-RPC Handler support
+  ---------------------------------------------------------------------}
+
+  { TJSONParamDef }
+
+  TJSONParamDef = Class(TCollectionItem)
+  private
+    FName: TJSONStringType;
+    FRequired: Boolean;
+    FType: TJSONtype;
+    procedure SetName(const AValue: TJSONStringType);
+  public
+    Constructor Create(ACollection : TCollection); override;
+    Procedure Assign(Source : TPersistent); override;
+  Published
+    Property Name : TJSONStringType Read FName Write SetName;
+    Property DataType : TJSONtype Read FType Write FType default jtString;
+    Property Required : Boolean Read FRequired Write FRequired default True;
+  end;
+
+  { TJSONParamDefs }
+
+  TJSONParamDefs = Class(TCollection)
+  private
+    function GetP(AIndex : Integer): TJSONParamDef;
+    procedure SetP(AIndex : Integer; const AValue: TJSONParamDef);
+  Public
+    Function AddParamDef(Const AName : TJSONStringType; AType : TJSONType = jtString) : TJSONParamDef;
+    Function IndexOfParamDef(Const AName : TJSONStringType) : Integer;
+    Function FindParamDef(Const AName : TJSONStringType) : TJSONParamDef;
+    Function ParamDefByName(Const AName : TJSONStringType) : TJSONParamDef;
+    Property ParamDefs[AIndex : Integer] : TJSONParamDef Read GetP Write SetP; default;
+  end;
+
+  { TCustomJSONRPCHandler }
+  TJSONParamErrorEvent = Procedure (Sender : TObject; Const E : Exception; Var Fatal : boolean) of Object;
+  TJSONRPCOption = (jroCheckParams,jroObjectParams,jroArrayParams);
+  TJSONRPCOptions = set of TJSONRPCOption;
+
+  TJSONRPCCallContext = Class(TObject);
+
+  TCustomJSONRPCHandler = Class(TComponent)
+  private
+    FAfterExecute: TNotifyEvent;
+    FBeforeExecute: TNotifyEvent;
+    FOnParamError: TJSONParamErrorEvent;
+    FOptions: TJSONRPCOptions;
+    FParamDefs: TJSONParamDefs;
+    procedure SetParamDefs(const AValue: TJSONParamDefs);
+  Protected
+    function CreateParamDefs: TJSONParamDefs; virtual;
+    Procedure DoCheckParams(Const Params : TJSONData); virtual;
+    Function DoExecute(Const Params : TJSONData; AContext : TJSONRPCCallContext): TJSONData; virtual;
+    Property BeforeExecute : TNotifyEvent Read FBeforeExecute Write FBeforeExecute;
+    Property AfterExecute : TNotifyEvent Read FAfterExecute Write FAfterExecute;
+    Property OnParamError :TJSONParamErrorEvent Read FOnParamError Write FONParamError;
+    Property Options : TJSONRPCOptions Read FOptions Write FOptions;
+  Public
+    Constructor Create(AOwner : TComponent); override;
+    Destructor Destroy; override;
+    Procedure CheckParams(Const Params : TJSONData);
+    Function Execute(Const Params : TJSONData; AContext : TJSONRPCCallContext = Nil) : TJSONData;
+    Property ParamDefs : TJSONParamDefs Read FParamDefs Write SetParamDefs;
+  end;
+  TCustomJSONRPCHandlerClass = Class of TCustomJSONRPCHandler;
+
+  TJSONRPCEvent = Procedure (Sender : TObject; Const Params : TJSONData; Out Res : TJSONData) of object;
+
+  { TJSONRPCHandler }
+
+  TJSONRPCHandler = Class(TCustomJSONRPCHandler)
+  private
+    FOnExecute: TJSONRPCEvent;
+  protected
+    Function DoExecute(Const Params : TJSONData; AContext : TJSONRPCCallContext): TJSONData; override;
+  Published
+    Property OnExecute : TJSONRPCEvent Read FOnExecute Write FOnExecute;
+    Property BeforeExecute;
+    Property AfterExecute;
+    Property OnParamError;
+    Property Options;
+    Property ParamDefs;
+  end;
+
+  { TJSONRPCEcho }
+
+  TJSONRPCEcho = Class(TCustomJSONRPCHandler)
+  Protected
+    Function DoExecute(Const Params : TJSONData;AContext : TJSONRPCCallContext): TJSONData; override;
+  end;
+
+{ ---------------------------------------------------------------------
+  JSON-RPC dispatcher support
+  ---------------------------------------------------------------------}
+
+  TJSONRPCDispatchOption = (jdoSearchRegistry, // Check JSON Handler registry
+                            jdoSearchOwner, // Check owner (usually webmodule) for request handler
+                            jdoJSONRPC1, // Allow JSON RPC-1
+                            jdoJSONRPC2, // Allow JSON RPC-2
+                            jdoRequireClass, // Require class name (as in Ext.Direct)
+                            jdoNotifications, // Allow JSON Notifications
+                            jdoStrictNotifications // Error if notification returned result. Default is to discard result.
+                            );
+  TJSONRPCDispatchOptions = set of TJSONRPCDispatchOption;
+
+Const
+  DefaultDispatchOptions =  [jdoSearchOwner,jdoJSONRPC1,jdoJSONRPC2,jdoNotifications];
+
+Type
+  TDispatchRequestEvent = Procedure(Sender : TObject; Const AClassName,AMethod : TJSONStringType; Const Params : TJSONData) of object;
+  TFindRPCHandlerEvent =  Procedure(Sender : TObject; Const AClassName,AMethod : TJSONStringType; Out Handler : TCustomJSONRPCHandler) of object;
+
+  { TCustomJSONRPCDispatcher }
+
+  TCustomJSONRPCDispatcher = Class(TComponent)
+  private
+    FFindHandler: TFindRPCHandlerEvent;
+    FOnDispatchRequest: TDispatchRequestEvent;
+    FOnEndBatch: TNotifyEvent;
+    FOnStartBatch: TNotifyEvent;
+    FOptions: TJSONRPCDispatchOptions;
+  Protected
+    // Find handler. If none found, nil is returned. Executes OnFindHandler if needed.
+    // On return 'DoFree' must be set to indicate that the hand
+    Function FindHandler(Const AClassName,AMethodName : TJSONStringType;AContext : TJSONRPCCallContext; Out FreeObject : TComponent) : TCustomJSONRPCHandler; virtual;
+    // Execute method. Finds handler, and returns response.
+    Function ExecuteMethod(Const AClassName, AMethodName : TJSONStringType; Params,ID : TJSONData; AContext : TJSONRPCCallContext) : TJSONData; virtual;
+    // Check and Execute a single request. Exceptions are caught and converted to request error object.
+    function ExecuteRequest(ARequest: TJSONData;AContext : TJSONRPCCallContext): TJSONData;
+    // Execute requests, returns responses in same format as requests (single or array)
+    Function DoExecute(Requests : TJSONData;AContext : TJSONRPCCallContext) : TJSONData; virtual;
+    // Check if single request corresponds to specs.
+    // Returns an error object if an error was found.
+    // if request is OK, then transaction id, classname, method and params *must* be returned.
+    // The returned transaction id, method, classname and params will be ignored if there is an error.
+    function CheckRequest(Request: TJSONData; Out AClassName, AMethodName : TJSONStringType; Out ID, Params : TJSONData): TJSONData; virtual;
+    // Check if requests are OK (check if JSON2 is allowed for array).
+    Function CheckRequests(Requests : TJSONData) : TJSONData; virtual;
+    // Format result of a single request. Result is returned to the client, possibly in an array if multiple requests were received in batch.
+    Function FormatResult(const AClassName, AMethodName: TJSONStringType;  const Params, ID, Return: TJSONData) : TJSONData; virtual;
+    // Hooks for user.
+    Property OnStartBatch : TNotifyEvent Read FOnStartBatch Write FOnStartBatch;
+    Property OnDispatchRequest : TDispatchRequestEvent Read FOnDispatchRequest Write FOnDispatchRequest;
+    Property OnFindHandler : TFindRPCHandlerEvent Read FFindHandler Write FFindHandler;
+    Property OnEndBatch : TNotifyEvent Read FOnEndBatch Write FOnEndBatch;
+    Property Options : TJSONRPCDispatchOptions Read FOptions Write FOptions default DefaultDispatchOptions;
+    Class Function MethodProperty : String; virtual;
+    Class Function ClassNameProperty : String; virtual;
+    Class Function ParamsProperty : String; virtual;
+  Public
+    Constructor Create(AOwner : TComponent); override;
+    Class Function TransactionProperty : String; virtual;
+    Function Execute(Requests : TJSONData;AContext : TJSONRPCCallContext = Nil) : TJSONData;
+  end;
+
+  TJSONRPCDispatcher = Class(TCustomJSONRPCDispatcher)
+  Published
+    Property OnStartBatch;
+    Property OnDispatchRequest;
+    Property OnFindHandler;
+    Property OnEndBatch;
+    Property Options;
+  end;
+
+
+{ ---------------------------------------------------------------------
+  Factory support
+  ---------------------------------------------------------------------}
+
+
+   { TJSONRPCHandlerDef }
+
+  TDataModuleClass = Class of TDataModule; // For the time being. As of rev 15343 it is in classes unit
+
+  TBeforeCreateJSONRPCHandlerEvent = Procedure (Sender : TObject; Var AClass : TCustomJSONRPCHandlerClass) of object;
+  TJSONRPCHandlerEvent = Procedure (Sender : TObject; AHandler : TCustomJSONRPCHandler) of object;
+
+  TJSONRPCHandlerDef = Class(TCollectionItem)
+  private
+    FAfterCreate: TJSONRPCHandlerEvent;
+    FArgumentCount: Integer;
+    FBeforeCreate: TBeforeCreateJSONRPCHandlerEvent;
+    FPClass: TCustomJSONRPCHandlerClass;
+    FDataModuleClass : TDataModuleClass;
+    FHandlerMethodName: TJSONStringType;
+    FHandlerClassName: TJSONStringType;
+    procedure CheckNames(const AClassName, AMethodName: TJSONStringType);
+    procedure SetFPClass(const AValue: TCustomJSONRPCHandlerClass);
+    procedure SetHandlerClassName(const AValue: TJSONStringType);
+    procedure SetHandlerMethodName(const AValue: TJSONStringType);
+  protected
+    Function CreateInstance(AOwner : TComponent; Out AContainer : TComponent) : TCustomJSONRPCHandler; virtual;
+    Property DataModuleClass : TDataModuleClass Read FDataModuleClass;
+  Public
+    Property HandlerClassName : TJSONStringType Read FHandlerClassName Write SetHandlerClassName;
+    Property HandlerMethodName : TJSONStringType Read FHandlerMethodName Write SetHandlerMethodName;
+    Property HandlerClass : TCustomJSONRPCHandlerClass Read FPClass Write SetFPClass;
+    Property BeforeCreate : TBeforeCreateJSONRPCHandlerEvent Read FBeforeCreate Write FBeforeCreate;
+    Property AfterCreate : TJSONRPCHandlerEvent Read FAfterCreate Write FAfterCreate;
+    Property ArgumentCount : Integer Read FArgumentCount Write FArgumentCount;
+  end;
+
+  { TJSONRPCHandlerDefs }
+
+  TJSONRPCHandlerDefs = Class(TCollection)
+  private
+    function GetH(Index : Integer): TJSONRPCHandlerDef;
+    procedure SetH(Index : Integer; const AValue: TJSONRPCHandlerDef);
+  Public
+    Function IndexOfHandler(Const AClassName,AMethodName : TJSONStringType) : Integer;
+    Function AddHandler(Const AClassName,AMethodName : TJSONStringType) : TJSONRPCHandlerDef; overload;
+    Function AddHandler(Const AClassName,AMethodName : TJSONStringType; AClass : TCustomJSONRPCHandlerClass) : TJSONRPCHandlerDef; overload;
+    Property HandlerDefs[Index : Integer] : TJSONRPCHandlerDef Read GetH Write SetH; default;
+  end;
+
+  { TCustomJSONRPCHandlerManager }
+
+  TCustomJSONRPCHandlerManager = Class(TComponent)
+  Private
+    FRegistering: Boolean;
+  Protected
+    procedure Initialize; virtual;
+    // Handler support
+    Procedure RemoveHandlerDef(Const Index : Integer); virtual; abstract;
+    function AddHandlerDef(Const AClassName,AMethodName : TJSONStringType) : TJSONRPCHandlerDef; virtual; abstract;
+    function IndexOfHandlerDef(Const AClassName,AMethodName : TJSONStringType) : Integer; virtual; abstract;
+    function GetHandlerDef(Index : Integer): TJSONRPCHandlerDef; virtual; abstract;
+    function GetHandlerDefCount: Integer; virtual; abstract;
+  Public
+    // Handler support
+    Procedure UnregisterHandler(Const AClassName, AMethodName : TJSONStringType);
+    Procedure RegisterDatamodule(Const AClass : TDatamoduleClass; Const AHandlerClassName : TJSONStringType);
+    Function RegisterHandler(Const AMethodName : TJSONStringType; AClass : TCustomJSONRPCHandlerClass; AArgumentCount : Integer = 0) : TJSONRPCHandlerDef; overload;
+    Function RegisterHandler(Const AClassName,AMethodName : TJSONStringType; AClass : TCustomJSONRPCHandlerClass; AArgumentCount : Integer = 0) : TJSONRPCHandlerDef; overload;
+    Function FindHandlerDefByName(Const AClassName,AMethodName : TJSONStringType) : TJSONRPCHandlerDef;
+    Function GetHandlerDefByName(Const AClassName,AMethodName : TJSONStringType) : TJSONRPCHandlerDef;
+    Function GetHandler(Const ADef : TJSONRPCHandlerDef; AOwner : TComponent; Out AContainer : TComponent): TCustomJSONRPCHandler;
+    Function GetHandler(Const AClassName,AMethodName : TJSONStringType; AOwner : TComponent; Out AContainer : TComponent): TCustomJSONRPCHandler;
+    Procedure GetClassNames (List : TStrings); // Should be a stringlist of TJSONStringType
+    Procedure GetMethodsOfClass(Const AClassName : TJSONStringType; List : TStrings); // Should be a stringlist of TJSONStringType
+    // properties
+    Property Registering : Boolean Read FRegistering;
+    Property HandlerCount : Integer Read GetHandlerDefCount;
+    Property HandlerDefs[Index : Integer] : TJSONRPCHandlerDef Read GetHandlerDef;
+  end;
+  TCustomJSONRPCHandlerManagerClass = Class of TCustomJSONRPCHandlerManager;
+
+  { TJSONRPCHandlerManager }
+
+  TJSONRPCHandlerManager = Class(TCustomJSONRPCHandlerManager)
+  Private
+    FHandlerDefs : TJSONRPCHandlerDefs;
+  Protected
+    Procedure RemoveHandlerDef(Const Index : Integer); override;
+    function AddHandlerDef(Const AClassName,AMethodName : TJSONStringType) : TJSONRPCHandlerDef; override;
+    function IndexOfHandlerDef(Const AClassName,AMethodName : TJSONStringType) : Integer; override;
+    function GetHandlerDef(Index : Integer): TJSONRPCHandlerDef; override;
+    function GetHandlerDefCount: Integer; override;
+  Public
+    Constructor Create(AOwner : TComponent); override;
+    Destructor Destroy; override;
+  end;
+
+
+{ ---------------------------------------------------------------------
+  Auxiliary stuff
+  ---------------------------------------------------------------------}
+
+
+  EJSONRPC = Class(Exception);
+  TJSONErrorObject = Class(TJSONObject);
+
+// Raise EJSONRPC exceptions.
+Procedure JSONRPCError(Msg : String);
+Procedure JSONRPCError(Fmt : String; Args : Array of const);
+
+// Create an 'Error' object for an error response.
+function CreateJSONErrorObject(Const AMessage : String; Const ACode : Integer) : TJSONObject;
+
+// Create a JSON RPC 2 error response object containing an 'Error' object.
+// Result is of type TJSONErrorObject
+function CreateJSON2ErrorResponse(Const AMessage : String; Const ACode : Integer; ID : TJSONData = Nil; idname : TJSONStringType = 'id' ) : TJSONObject;
+function CreateJSON2ErrorResponse(Const AFormat : String; Args : Array of const; Const ACode : Integer; ID : TJSONData = Nil; idname : TJSONStringType = 'id') : TJSONObject;
+// Examines Req (request) and returns Error or an array of clones of Error)
+Function CreateErrorForRequest(Const Req,Error : TJSONData) : TJSONData;
+
+// Return TCustomJSONRPCHandlerManager instance to use for managing JSON-RPC handler.
+
+Function JSONRPCHandlerManager : TCustomJSONRPCHandlerManager;
+
+// Class that will be created. Must be set before first call to JSONRPCHandlerManager.
+Var
+  JSONRPCHandlerManagerClass : TCustomJSONRPCHandlerManagerClass = TJSONRPCHandlerManager;
+
+
+Const
+  // JSON RPC 2.0 error codes
+  EJSONRPCParseError     = -32700;
+  EJSONRPCInvalidRequest = -32600;
+  EJSONRPCMethodNotFound = -32601;
+  EJSONRPCInvalidParams  = -32602;
+  EJSONRPCInternalError  = -32603;
+
+
+resourcestring
+  SErrDuplicateParam  = 'Duplicate JSON-RPC Parameter name';
+  SErrUnknownParamDef = 'Unknown parameter definition: "%s"';
+  SErrParams = 'Error checking JSON-RPC parameters: "%s"';
+  SErrParamsMustBeArrayorObject = 'Parameters must be passed in an object or an array.';
+  SErrParamsMustBeObject = 'Parameters must be passed in an object.';
+  SErrParamsMustBeArray  = 'Parameters must be passed in an array.';
+  SErrRequestMustBeObject = 'JSON-RPC Request must be an object.';
+  SErrNoIDProperty = 'No "id" property found in request.';
+  SErrInvalidIDProperty = 'Type of "id" property is not correct.';
+  SErrNoJSONRPCProperty = 'No "jsonrpc" property in request.';
+  SErrInvalidJSONRPCProperty = 'Type or value of "jsonrpc" property is not correct.';
+  SErrNoMethodName = 'Cannot determine method: No "%s" property found in request.';
+  SErrNoClassName  = 'Cannot determine class: No "%s" property found in request.';
+  SErrNoParams = 'Cannot determine parameters: No "%s" property found in request.';
+  SErrInvalidMethodType = 'Type of "%s" property in request is not correct.';
+  SErrInvalidClassNameType = 'Type of "%s" property in request is not correct.';
+  SErrJSON2NotAllowed = 'JSON RPC 2 calls are not allowed.';
+  SErrJSON1NotAllowed = 'JSON RPC 1 calls are not allowed.';
+  SErrNoResponse = 'No response received from non-notification method "%s".';
+  SErrResponseFromNotification = 'A response was received from a notification method "%s".';
+  SErrInvalidMethodName = 'No method "%s" was found.';
+  SErrInvalidClassMethodName = 'No class "%s" with method "%s" was found.';
+  SErrDuplicateJSONRPCClassHandlerName = 'Duplicate JSON-RPC handler for class "%s" with method "%s".';
+  SErrDuplicateJSONRPCHandlerName = 'Duplicate JSON-RPC handler for method "%s".';
+  SErrUnknownJSONRPCClassMethodHandler = 'Unknown JSON-RPC handler for class "%s", method "%s".';
+  SErrUnknownJSONRPCMethodHandler = 'Unknown JSON-RPC handler for method "%s".';
+  SErrDuplicateRPCCLassMethodHandler = 'Duplicate JSON-RPC handler for class "%s", method "%s".';
+  SErrDuplicateRPCMethodHandler = 'Duplicate JSON-RPC handler for method "%s".';
+  SErrNoDispatcher = 'No method dispatcher available to handle request.';
+
+
+implementation
+
+function CreateJSONErrorObject(Const AMessage : String; Const ACode : Integer) : TJSONObject;
+
+begin
+  Result:=TJSONObject.Create(['code',ACode,'message',AMessage])
+end;
+
+function CreateJSON2ErrorResponse(Const AMessage : String; Const ACode : Integer; ID : TJSONData = Nil; idname : TJSONStringType = 'id' ) : TJSONObject;
+
+begin
+  If (ID=Nil) then
+    ID:=TJSONNull.Create;
+  Result:=TJSONErrorObject.Create(['jsonrpc','2.0','error',CreateJSONErrorObject(AMessage,ACode),idname,ID]);
+end;
+
+function CreateJSON2ErrorResponse(Const AFormat : String; Args : Array of const; Const ACode : Integer; ID : TJSONData = Nil; idname : TJSONStringType = 'id' ) : TJSONObject;
+
+begin
+  If (ID=Nil) then
+    ID:=TJSONNull.Create;
+  Result:=TJSONErrorObject.Create(['jsonrpc','2.0','error',CreateJSONErrorObject(Format(AFormat,Args),ACode),idname,ID]);
+end;
+
+Function CreateErrorForRequest(Const Req,Error : TJSONData) : TJSONData;
+
+Var
+  I : Integer;
+
+begin
+  if Req is TJSONArray then
+    begin
+    Result:=TJSONArray.Create;
+    TJSONArray(Result).Add(Error);
+    For I:=1 to TJSONArray(Req).Count-1 do
+      TJSONArray(Result).Add(Error.Clone);
+    end
+  else
+    Result:=Error;
+end;
+
+
+Var
+  TheHandler : TCustomJSONRPCHandlerManager;
+
+function JSONRPCHandlerManager: TCustomJSONRPCHandlerManager;
+begin
+  If (TheHandler=Nil) then
+    TheHandler:=JSONRPCHandlerManagerClass.Create(Nil);
+  JSONRPCHandlerManager:=TheHandler;
+end;
+
+Procedure JSONRPCError(Msg : String);
+
+begin
+  Raise EJSONRPC.Create(Msg);
+end;
+
+Procedure JSONRPCError(Fmt : String; Args : Array of const);
+
+begin
+  Raise EJSONRPC.CreateFmt(Fmt,Args);
+end;
+
+{ TJSONParamDef }
+
+procedure TJSONParamDef.SetName(const AValue: TJSONStringType);
+begin
+  if FName=AValue then exit;
+  If Assigned(Collection) and (Collection is TJSONParamDefs) then
+    if (Collection as TJSONParamDefs).FindParamDef(AValue)<>Nil then
+      JSONRPCError(SErrDuplicateParam,[AValue]);
+  FName:=AValue;
+end;
+
+constructor TJSONParamDef.Create(ACollection: TCollection);
+begin
+  inherited Create(ACollection);
+  FType:=jtString;
+  FRequired:=True;
+end;
+
+procedure TJSONParamDef.Assign(Source: TPersistent);
+
+Var
+  P : TJSONParamDef;
+
+begin
+  If Source is TJSONParamDef then
+    begin
+    P:=TJSONParamDef(Source);
+    FType:=P.DataType;
+    FName:=P.Name;
+    FRequired:=P.Required;
+    end
+  else
+    inherited Assign(Source);
+end;
+
+{ TJSONParamDefs }
+
+function TJSONParamDefs.GetP(AIndex : Integer): TJSONParamDef;
+begin
+  Result:=TJSONParamDef(Items[AIndex]);
+end;
+
+procedure TJSONParamDefs.SetP(AIndex : Integer; const AValue: TJSONParamDef);
+begin
+  Items[AIndex]:=AValue;
+end;
+
+function TJSONParamDefs.AddParamDef(const AName: TJSONStringType; AType: TJSONType
+  ): TJSONParamDef;
+begin
+  Result:=Add as TJSONParamDef;
+  try
+    Result.Name:=AName;
+    Result.DataType:=Atype;
+  except
+    FReeAndNil(Result);
+    Raise;
+  end;
+end;
+
+function TJSONParamDefs.IndexOfParamDef(const AName: TJSONStringType): Integer;
+begin
+  Result:=Count-1;
+  While (Result>=0) and (CompareText(AName,GetP(result).Name)<>0) do
+    Dec(Result);
+end;
+
+function TJSONParamDefs.FindParamDef(const AName: TJSONStringType): TJSONParamDef;
+
+Var
+  I : integer;
+
+begin
+  I:=IndexOfParamDef(AName);
+  If (I=-1) then
+    Result:=Nil
+  else
+    Result:=GetP(I);
+end;
+
+function TJSONParamDefs.ParamDefByName(const AName: TJSONStringType): TJSONParamDef;
+begin
+  Result:=FindParamDef(AName);
+  If (Result=Nil) then
+    JSONRPCError(SErrUnknownParamDef,[AName]);
+end;
+
+{ TCustomJSONRPCHandler }
+
+procedure TCustomJSONRPCHandler.CheckParams(const Params: TJSONData);
+
+Var
+  B : Boolean;
+
+begin
+  Try
+    DoCheckParams(Params);
+  Except
+    On E : Exception do
+      begin
+      B:=True;
+      If Assigned(FonParamError) then
+        FonParamError(Self,E,B);
+      If B then
+        Raise;
+      end;
+  end;
+end;
+
+procedure TCustomJSONRPCHandler.SetParamDefs(const AValue: TJSONParamDefs);
+begin
+  if FParamDefs=AValue then exit;
+  FParamDefs.Assign(AValue);
+end;
+
+procedure TCustomJSONRPCHandler.DoCheckParams(const Params: TJSONData);
+begin
+  If (jroObjectParams in Options) and Not (Params is TJSONobject) then
+    JSONRPCError(SErrParams,[SErrParamsMustBeObject]);
+  If (jroArrayParams in Options) and Not (Params is TJSONArray) then
+    JSONRPCError(SErrParams,[SErrParamsMustBeArray]);
+end;
+
+function TCustomJSONRPCHandler.DoExecute(Const Params: TJSONData;AContext : TJSONRPCCallContext): TJSONData;
+begin
+  Result:=Nil;
+end;
+
+constructor TCustomJSONRPCHandler.Create(AOwner: TComponent);
+begin
+  inherited Create(AOwner);
+  FParamDefs:=CreateParamDefs;
+end;
+
+destructor TCustomJSONRPCHandler.Destroy;
+begin
+  FreeAndNil(FParamDefs);
+  inherited Destroy;
+end;
+
+function TCustomJSONRPCHandler.CreateParamDefs : TJSONParamDefs;
+
+begin
+  Result:=TJSONParamDefs.Create(TJSONParamDef);
+end;
+
+function TCustomJSONRPCHandler.Execute(Const Params: TJSONData;AContext : TJSONRPCCallContext = Nil): TJSONData;
+begin
+  If Assigned(FBeforeExecute) then
+    FBeforeExecute(Self);
+  if (jroCheckParams in Options) then
+    CheckParams(Params);
+  Result:=DoExecute(Params,AContext);
+  If Assigned(FAfterExecute) then
+    FAfterExecute(Self);
+end;
+
+{ TJSONRPCHandler }
+
+function TJSONRPCHandler.DoExecute(const Params: TJSONData;AContext : TJSONRPCCallContext): TJSONData;
+begin
+  If Assigned(FOnExecute) then
+    FOnExecute(Self,Params,Result);
+end;
+
+{ TJSONRPCEcho }
+
+function TJSONRPCEcho.DoExecute(const Params: TJSONData;AContext : TJSONRPCCallContext): TJSONData;
+
+Var
+  I : Integer;
+  A : TJSONArray;
+  S : TJSONStringType;
+
+begin
+  If Params is TJSONArray then
+    begin
+    A:=Params as TJSONArray;
+    S:='';
+    For I:=0 to A.Count-1 do
+      begin
+      if (S<>'') then
+        S:=S+' ';
+      S:=S+A.Items[i].AsString;
+      end;
+    end
+  else If Params.JSONType in [jtObject,jtNumber] then
+    S:=Params.AsJSON
+  else
+    S:=Params.AsString;
+  Result:=TJSONString.Create(S);
+end;
+
+{ TCustomJSONRPCDispatcher }
+
+function TCustomJSONRPCDispatcher.FindHandler(const AClassName, AMethodName: TJSONStringType;AContext : TJSONRPCCallContext;Out FreeObject : TComponent): TCustomJSONRPCHandler;
+
+Var
+  C : TComponent;
+  D : TJSONRPCHandlerDef;
+
+
+begin
+  Result:=Nil;
+  FreeObject:=Nil;
+  If Assigned(Owner) and ((AClassName='') or (CompareText(AClassName,Owner.name)=0)) then
+    begin
+    C:=Owner.FindComponent(AMethodName);
+    If C is TCustomJSONRPCHandler then
+      Result:=TCustomJSONRPCHandler(C);
+    end;
+  If (Result=Nil) and (jdoSearchRegistry in Options) then
+    begin
+    D:=JSONRPCHandlerManager.FindHandlerDefByName(AClassName,AMethodName);
+    If Assigned(D) then
+      Result:=JSONRPCHandlerManager.GetHandler(D,Self,FreeObject);
+    end;
+ If (Result=Nil) and Assigned(FFindHandler) then
+    begin
+    FFindhandler(Self,AClassName,AMethodName,Result);
+    FreeObject:=Nil
+    end;
+end;
+
+function TCustomJSONRPCDispatcher.ExecuteMethod(Const AClassName,AMethodName: TJSONStringType;
+  Params,ID: TJSONData;AContext : TJSONRPCCallContext): TJSONData;
+
+Var
+  H : TCustomJSONRPCHandler;
+  FreeObject : TComponent;
+
+begin
+  H:=FindHandler(AClassName,AMethodName,AContext,FreeObject);
+  If (H=Nil) then
+    if (AClassName='') then
+      Exit(CreateJSON2ErrorResponse(SErrInvalidMethodName,[AMethodName],EJSONRPCMethodNotFound,ID.Clone,transactionProperty))
+    else
+      Exit(CreateJSON2ErrorResponse(SErrInvalidClassMethodName,[AClassName,AMethodName],EJSONRPCMethodNotFound,ID.Clone,transactionProperty));
+  try
+    If Assigned(FOndispatchRequest) then
+      FOndispatchRequest(Self,AClassName,AMethodName,Params);
+    Result:=H.Execute(Params,AContext);
+  finally
+    If Assigned(FreeObject) then
+      FreeAndNil(FreeObject);
+  end;
+end;
+
+function TCustomJSONRPCDispatcher.FormatResult(Const AClassName, AMethodName: TJSONStringType;
+Const Params,ID, Return : TJSONData) : TJSONData;
+
+begin
+  Result:=TJSONObject.Create(['result',Return,'error',TJSonNull.Create,transactionproperty,ID.Clone]);
+  if jdoJSONRPC2 in options then
+    TJSONObject(Result).Add('jsonrpc','2.0');
+end;
+
+function TCustomJSONRPCDispatcher.ExecuteRequest(ARequest: TJSONData;AContext : TJSONRPCCallContext): TJSONData;
+
+Var
+  C,M : TJSONStringType;
+  Id,P : TJSONData;
+
+
+begin
+  Result:=Nil;
+  try
+    Result:=CheckRequest(ARequest,C,M,ID,P);
+    If (Result=Nil) then
+      begin
+      Result:=ExecuteMethod(C,M,P,ID,AContext);
+      // Do some sanity checks.
+      If (Result=Nil) then
+        begin
+        // No response, and a response was expected.
+        if (ID<>Nil) or not (jdoNotifications in Options) then
+          Result:=CreateJSON2ErrorResponse(SErrNoResponse,[M],EJSONRPCInternalError,ID,transactionProperty);
+        end
+      else
+        begin
+        // A response was received, and no response was expected.
+        if ((ID=Nil) or (ID is TJSONNull))  and (jdoStrictNotifications in Options) then
+          Result:=CreateJSON2ErrorResponse(SErrResponseFromNotification,[M],EJSONRPCInternalError,ID,transactionProperty);
+        If (ID=Nil) or (ID is TJSONNull) then // Notification method, discard result.
+          FreeAndNil(Result);
+        end;
+      end;
+    If Assigned(Result) and not (Result is TJSONErrorObject) then
+      begin
+      Result:=FormatResult(C,M,P,ID,Result);
+      end;
+  except
+    // Something went really wrong if we end up here.
+    On E : Exception do
+      begin
+      If (Result<>Nil) then
+        FreeAndNil(Result);
+      If Assigned(ID) then
+        Result:=CreateJSON2ErrorResponse(E.Message,EJSONRPCInternalError,ID.Clone,transactionproperty)
+      else
+        Result:=CreateJSON2ErrorResponse(E.Message,EJSONRPCInternalError,Nil,transactionproperty)
+      end;
+  end;
+end;
+
+function TCustomJSONRPCDispatcher.DoExecute(Requests: TJSONData;AContext : TJSONRPCCallContext): TJSONData;
+
+Var
+  A : TJSONArray;
+  Res : TJSONData;
+  I : Integer;
+
+begin
+  Result:=Nil;
+  If Requests is TJSONArray then
+    begin
+    A:=Requests as TJSONArray;
+    Result:=TJSONArray.Create();
+    For I:=0 to A.Count-1 do
+      begin
+      Res:=ExecuteRequest(A[i],AContext);
+      If (Res<>Nil) then
+        TJSONArray(Result).Add(Res);
+      end;
+    end
+  else
+    Result:=ExecuteRequest(Requests,ACOntext);
+end;
+
+
+function TCustomJSONRPCDispatcher.CheckRequest(Request: TJSONData; Out AClassName,AMethodName : TJSONStringType; Out ID, Params : TJSONData): TJSONData;
+
+Var
+  O : TJSONObject;
+  I : Integer;
+  D : TJSONData;
+  OJ2 : Boolean;
+
+
+begin
+  AMethodName:='';
+  AClassName:='';
+  ID:=Nil;
+  Params:=Nil;
+  Result:=Nil;
+  If Not (Request is TJSONObject) then
+    Exit(CreateJSON2ErrorResponse(SErrRequestMustBeObject,EJSONRPCInvalidRequest,Nil,transactionproperty));
+  O:=TJSONObject(Request);
+  // Get ID object, if it exists.
+  I:=O.IndexOfName(TransactionProperty);
+  If (I<>-1) then
+    ID:=O.Items[i];
+  // Check ID
+  If (ID=Nil) and not (jdoNotifications in Options) then
+    Exit(CreateJSON2ErrorResponse(SErrNoIDProperty,EJSONRPCInvalidRequest,Nil,transactionproperty));
+  OJ2:=(jdoJSONRPC2 in Options) and not (jdoJSONRPC1 in Options);
+  If OJ2 then
+    begin
+    if Assigned(ID) and not (ID.JSONType in [jtNull,jtString,jtNumber]) then
+      Exit(CreateJSON2ErrorResponse(SErrINvalidIDProperty,EJSONRPCInvalidRequest,Nil,transactionproperty));
+    // Check presence and value of jsonrpc property
+    I:=O.IndexOfName('jsonrpc');
+    If (I=-1) then
+      Exit(CreateJSON2ErrorResponse(SErrNoJSONRPCProperty,EJSONRPCInvalidRequest,ID,transactionproperty));
+    If (O.Items[i].JSONType<>jtString) or (O.Items[i].AsString<>'2.0') then
+      Exit(CreateJSON2ErrorResponse(SErrInvalidJSONRPCProperty,EJSONRPCInvalidRequest,ID,transactionproperty));
+    end;
+  // Get method name, if it exists.
+  I:=O.IndexOfName(MethodProperty);
+  If (I<>-1) then
+    D:=O.Items[i]
+  else
+    Exit(CreateJSON2ErrorResponse(SErrNoMethodName,[MethodProperty],EJSONRPCInvalidRequest,ID,transactionproperty));
+  // Check if it is a string
+  if Not (D is TJSONString) then
+    Exit(CreateJSON2ErrorResponse(SErrInvalidMethodType,[MethodProperty],EJSONRPCInvalidRequest,ID,transactionproperty));
+  AMethodName:=D.AsString;
+  If (AMethodName='') then
+    Exit(CreateJSON2ErrorResponse(SErrNoMethodName,[MethodProperty],EJSONRPCInvalidRequest,ID,transactionproperty));
+  // Get class name, if it exists and is required
+  If (ClassNameProperty<>'') then
+    begin
+    I:=O.IndexOfName(ClassNameProperty);
+    If (I<>-1) then
+      D:=O.Items[i]
+    else if (jdoRequireClass in options) then
+      Exit(CreateJSON2ErrorResponse(SErrNoClassName,[ClassNameProperty],EJSONRPCInvalidRequest,ID,transactionproperty));
+    // Check if it is a string
+    if Not (D is TJSONString) then
+      Exit(CreateJSON2ErrorResponse(SErrInvalidClassNameType,[ClassNameProperty],EJSONRPCInvalidRequest,ID,transactionproperty));
+    AClassName:=D.AsString;
+    If (AMethodName='') and (jdoRequireClass in options)  then
+      Exit(CreateJSON2ErrorResponse(SErrNoClassName,[ClassNameProperty],EJSONRPCInvalidRequest,ID,transactionproperty));
+    end;
+  // Get params, if they exist
+  I:=O.IndexOfName(ParamsProperty);
+  If (I<>-1) then
+    D:=O.Items[i]
+  else
+    Exit(CreateJSON2ErrorResponse(SErrNoParams,[ParamsProperty],EJSONRPCInvalidParams,ID,transactionproperty));
+  if OJ2 then
+    begin
+    // Allow array or object
+    If Not (D.JSONType in [jtArray,jtObject]) then
+      Exit(CreateJSON2ErrorResponse(SErrParamsMustBeArrayorObject,EJSONRPCInvalidParams,ID,transactionproperty));
+    end
+  else if not (jdoJSONRPC2 in Options) then
+    begin
+    // Allow only array
+    If Not (D.JSONType in [jtArray]) then
+      Exit(CreateJSON2ErrorResponse(SErrParamsMustBeArray,EJSONRPCInvalidParams,ID,transactionproperty));
+    end;
+  Params:=D;
+end;
+
+function TCustomJSONRPCDispatcher.CheckRequests(Requests: TJSONData): TJSONData;
+
+Var
+  A : TJSONArray;
+  O : TJSONObject;
+  ID : TJSONData;
+  I,J : Integer;
+
+begin
+  Result:=Nil;
+  If (Requests is TJSONArray) then
+    begin
+    A:=Requests as TJSONArray;
+    If Not (jdoJSONRPC2 in Options) then
+      begin
+      Result:=TJSONArray.Create;
+      For I:=0 to A.Count-1 do
+        begin
+        ID:=Nil;
+        If (A.Items[i] is TJSONObject) then
+          begin
+          O:=A.Objects[i];
+          J:=O.IndexOfName('id');
+          if (J<>-1) then
+            ID:=O.Items[J].Clone;
+          end;
+        TJSONArray(Result).Add(CreateJSON2ErrorResponse(SErrJSON2NotAllowed,EJSONRPCInvalidRequest,ID,transactionproperty));
+        end;
+      end;
+    end
+  else
+    If not (Requests is TJSONObject) then
+      Result:=CreateJSON2ErrorResponse(SErrRequestMustBeObject,EJSONRPCInvalidRequest,Nil,transactionproperty);
+end;
+
+class function TCustomJSONRPCDispatcher.TransactionProperty: String;
+begin
+  Result:='id'; // Do not localize
+end;
+
+class function TCustomJSONRPCDispatcher.MethodProperty: String;
+begin
+  Result:='method'; // Do not localize
+end;
+
+class function TCustomJSONRPCDispatcher.ClassNameProperty: String;
+begin
+  Result:=''; // Do not localize
+end;
+
+class function TCustomJSONRPCDispatcher.ParamsProperty: String;
+begin
+  Result:='params'; // Do not localize
+end;
+
+constructor TCustomJSONRPCDispatcher.Create(AOwner: TComponent);
+begin
+  inherited Create(AOwner);
+  FOptions:=DefaultDispatchOptions;
+end;
+
+function TCustomJSONRPCDispatcher.Execute(Requests: TJSONData;AContext : TJSONRPCCallContext = Nil): TJSONData;
+begin
+  If Assigned(FOnStartBatch) then
+    FOnStartBatch(Self);
+  Result:=CheckRequests(Requests);
+  if (Result=Nil) then // Form is OK and allowed.
+    Result:=DoExecute(Requests,AContext);
+  If Assigned(FOnEndBatch) then
+    FOnEndBatch(Self);
+end;
+
+{ TJSONRPCHandlerDef }
+
+procedure TJSONRPCHandlerDef.SetFPClass(const AValue: TCustomJSONRPCHandlerClass
+  );
+begin
+  FPClass:=AValue;
+end;
+
+procedure TJSONRPCHandlerDef.CheckNames(Const AClassName,AMethodName : TJSONStringType);
+
+Var
+  I : Integer;
+
+begin
+  If Assigned(Collection) and (Collection is TJSONRPCHandlerDefs) then
+    begin
+    I:=TJSONRPCHandlerDefs(Collection).IndexOfHandler(AClassName,AMethodName);
+    If (I<>-1) and (Collection.Items[i]<>Self) then
+      if (AClassName<>'') then
+        JSONRPCError(SErrDuplicateJSONRPCClassHandlerName,[AClassName,AMethodName])
+      else
+        JSONRPCError(SErrDuplicateJSONRPCHandlerName,[AClassName,AMethodName]);
+    end;
+end;
+
+procedure TJSONRPCHandlerDef.SetHandlerClassName(const AValue: TJSONStringType);
+begin
+  if FHandlerClassName=AValue then exit;
+  CheckNames(AValue,HandlerMethodName);
+  FHandlerClassName:=AValue;
+end;
+
+procedure TJSONRPCHandlerDef.SetHandlerMethodName(const AValue: TJSONStringType
+  );
+begin
+  if FHandlerMethodName=AValue then exit;
+  CheckNames(HandlerClassName,AValue);
+  FHandlerMethodName:=AValue;
+end;
+
+function TJSONRPCHandlerDef.CreateInstance(AOwner: TComponent; out
+  AContainer: TComponent): TCustomJSONRPCHandler;
+
+Var
+  AClass : TCustomJSONRPCHandlerClass;
+  DM : TDataModule;
+  C : TComponent;
+
+begin
+  Result:=Nil;
+  {$ifdef wmdebug}SendDebug(Format('Creating instance for %s',[Self.ProviderName]));{$endif}
+  If Assigned(FDataModuleClass) then
+    begin
+    {$ifdef wmdebug}SendDebug(Format('Creating datamodule from class %d ',[Ord(Assigned(FDataModuleClass))]));{$endif}
+    DM:=FDataModuleClass.Create(AOwner);
+    {$ifdef wmdebug}SendDebug(Format('Created datamodule from class %s ',[DM.ClassName]));{$endif}
+    C:=DM.FindComponent(FHandlerMethodName);
+    If (C<>Nil) and (C is TCustomJSONRPCHandler) then
+      Result:=TCustomJSONRPCHandler(C)
+    else
+      begin
+      FreeAndNil(DM);
+      JSONRPCError(SErrUnknownJSONRPCMethodHandler,[FHandlerMethodName]);
+      end;
+    end
+  else
+    DM:=TDataModule.CreateNew(AOwner,0);
+  AContainer:=DM;
+  If (Result=Nil) then
+    begin
+    {$ifdef wmdebug}SendDebug(Format('Creating from class pointer %d ',[Ord(Assigned(FPClass))]));{$endif}
+    AClass:=FPCLass;
+    If Assigned(FBeforeCreate) then
+      FBeforeCreate(Self,AClass);
+    Result:=AClass.Create(AContainer);
+    end;
+  If Assigned(FAfterCreate) then
+    FAfterCreate(Self,Result);
+end;
+
+{ TJSONRPCHandlerDefs }
+
+function TJSONRPCHandlerDefs.GetH(Index: Integer): TJSONRPCHandlerDef;
+begin
+  Result:=TJSONRPCHandlerDef(Items[Index]);
+end;
+
+procedure TJSONRPCHandlerDefs.SetH(Index: Integer;
+  const AValue: TJSONRPCHandlerDef);
+begin
+  Items[Index]:=AValue;
+end;
+
+function TJSONRPCHandlerDefs.AddHandler(const AClassName,
+  AMethodName: TJSONStringType): TJSONRPCHandlerDef;
+begin
+  If (IndexOfHandler(AClassName,AMethodName)<>-1) then
+    If (AClassName<>'') then
+      JSONRPCError(SErrDuplicateJSONRPCClassHandlerName,[AClassName,AMethodName])
+    else
+      JSONRPCError(SErrDuplicateJSONRPCHandlerName,[AMethodName]);
+  Result:=TJSONRPCHandlerDef(Add);
+  Result.FHandlerClassName:=AClassName;
+  Result.FHandlerMethodName:=AMethodName;
+end;
+
+function TJSONRPCHandlerDefs.AddHandler(const AClassName,
+  AMethodName: TJSONStringType; AClass: TCustomJSONRPCHandlerClass
+  ): TJSONRPCHandlerDef;
+begin
+  Result:=AddHandler(AClassName,AMethodName);
+  Result.HandlerClass:=AClass;
+end;
+
+function TJSONRPCHandlerDefs.IndexOfHandler(const AClassName,
+  AMethodName: TJSONStringType): Integer;
+
+  Function IsMatch(Index : Integer) : Boolean; inline;
+
+  Var
+    D : TJSONRPCHandlerDef;
+
+  begin
+    D:=GetH(Index);
+    Result:=((AClassName='') or
+             (CompareText(D.HandlerClassName,AClassName)=0)) and
+            (CompareText(AMethodName,D.HandlerMethodName)=0)
+  end;
+
+begin
+  Result:=Count-1;
+  While (Result>=0) and Not IsMatch(Result) do
+    Dec(Result)
+end;
+
+{ TCustomJSONRPCHandlerManager }
+
+procedure TCustomJSONRPCHandlerManager.Initialize;
+begin
+  // Do nothing
+end;
+
+procedure TCustomJSONRPCHandlerManager.UnregisterHandler(const AClassName,
+  AMethodName: TJSONStringType);
+
+Var
+  I : Integer;
+
+begin
+  I:=IndexOfHandlerDef(AClassName,AMethodName);
+  If (I<>-1) then
+    RemoveHandlerDef(I)
+  else
+    If (AClassName<>'') then
+      JSONRPCError(SErrUnknownJSONRPCClassMethodHandler,[AClassName,AMethodName])
+    else
+      JSONRPCError(SErrUnknownJSONRPCMethodHandler,[AMethodName]);
+end;
+
+procedure TCustomJSONRPCHandlerManager.RegisterDatamodule(
+  const AClass: TDatamoduleClass; const AHandlerClassName: TJSONStringType);
+
+Var
+  DM : TDatamodule;
+  I,J : Integer;
+  C : TComponent;
+  D : TJSONRPCHandlerDef;
+  B : Boolean;
+  CN : TJSONStringType;
+
+begin
+  B:=FRegistering;
+  try
+    FRegistering:=True;
+    DM:=AClass.Create(Self);
+    try
+      If (AHandlerClassName='') then
+        CN:=DM.Name
+      else
+        CN:=AHandlerClassName;
+      For I:=0 to DM.ComponentCount-1 do
+        begin
+        C:=DM.Components[i];
+        if C is TCustomJSONRPCHandler then
+          begin
+          J:=IndexOfHandlerDef(CN,C.Name);
+          If (J<>-1) then
+             JSONRPCError(SErrDuplicateRPCCLassMethodHandler,[CN,C.Name]);
+          D:=AddHandlerDef(CN,C.Name);
+          D.ArgumentCount:=TCustomJSONRPCHandler(C).ParamDefs.Count;
+          {$ifdef wmdebug}SendDebug('Registering provider '+C.Name);{$endif}
+          D.FDataModuleClass:=TDataModuleClass(DM.ClassType);
+          end;
+        end;
+    finally
+      DM.Free;
+    end;
+  finally
+    FRegistering:=B;
+  end;
+end;
+
+function TCustomJSONRPCHandlerManager.RegisterHandler(
+  const AMethodName: TJSONStringType;
+  AClass: TCustomJSONRPCHandlerClass;
+  AArgumentCount : Integer = 0): TJSONRPCHandlerDef;
+
+begin
+  Result:=RegisterHandler('',AMethodName,AClass,AArgumentCount);
+end;
+
+function TCustomJSONRPCHandlerManager.RegisterHandler(
+  const AClassName,AMethodName: String;
+  AClass: TCustomJSONRPCHandlerClass;
+  AArgumentCount : Integer = 0): TJSONRPCHandlerDef;
+
+Var
+  I : Integer;
+  B : Boolean;
+
+begin
+  B:=FRegistering;
+  try
+    FRegistering:=True;
+    I:=IndexOfHandlerDef(AClassName,AMethodname);
+    If (I<>-1) then
+      If (AClassName<>'') then
+        JSONRPCError(SErrDuplicateRPCCLassMethodHandler,[AClassName,AMethodName])
+      else
+        JSONRPCError(SErrDuplicateRPCMethodHandler,[AMethodName]);
+    Result:=AddHandlerDef(AClassName,AMEthodName);
+    Result.HandlerClass:=AClass;
+    Result.ArgumentCount:=AArgumentCount;
+  finally
+    FRegistering:=B;
+  end;
+end;
+
+function TCustomJSONRPCHandlerManager.FindHandlerDefByName(const AClassName,
+  AMethodName: TJSONStringType): TJSONRPCHandlerDef;
+
+Var
+  I : integer;
+
+begin
+  I:=IndexOfHandlerDef(AClassName,AMethodName);
+  If (I=-1) then
+    Result:=Nil
+  else
+    Result:=GetHandlerDef(I);
+end;
+
+function TCustomJSONRPCHandlerManager.GetHandlerDefByName(const AClassName,
+  AMethodName: TJSONStringType): TJSONRPCHandlerDef;
+begin
+  Result:=FindHandlerDefByName(AClassName,AMethodName);
+  If (Result=Nil) then
+    If (AClassName<>'') then
+      JSONRPCError(SErrUnknownJSONRPCClassMethodHandler,[AClassName,AMethodName])
+    else
+      JSONRPCError(SErrUnknownJSONRPCMethodHandler,[AMethodName]);
+end;
+
+function TCustomJSONRPCHandlerManager.GetHandler(
+  const ADef: TJSONRPCHandlerDef; AOwner: TComponent; out AContainer: TComponent
+  ): TCustomJSONRPCHandler;
+
+Var
+  O : TComponent;
+
+begin
+  If AOwner=Nil then
+    O:=Self
+  else
+    O:=AOwner;
+  Result:=ADef.CreateInstance(O,AContainer);
+end;
+
+function TCustomJSONRPCHandlerManager.GetHandler(const AClassName,
+  AMethodName: TJSONStringType; AOwner: TComponent; out AContainer: TComponent
+  ): TCustomJSONRPCHandler;
+
+Var
+  D : TJSONRPCHandlerDef;
+
+begin
+  D:=GetHandlerDefByname(AClassName,AMEthodName);
+  Result:=GetHandler(D,AOwner,AContainer);
+end;
+
+procedure TCustomJSONRPCHandlerManager.GetClassNames(List: TStrings);
+begin
+
+end;
+
+procedure TCustomJSONRPCHandlerManager.GetMethodsOfClass(
+  const AClassName: TJSONStringType; List: TStrings);
+begin
+
+end;
+
+{ TJSONRPCHandlerManager }
+
+procedure TJSONRPCHandlerManager.RemoveHandlerDef(const Index: Integer);
+begin
+  FHandlerDefs.Delete(Index);
+end;
+
+function TJSONRPCHandlerManager.AddHandlerDef(const AClassName,
+  AMethodName: TJSONStringType): TJSONRPCHandlerDef;
+begin
+  Result:=FHandlerDefs.AddHandler(AClassName,AMethodName);
+end;
+
+function TJSONRPCHandlerManager.IndexOfHandlerDef(const AClassName,
+  AMethodName: TJSONStringType): Integer;
+begin
+  Result:=FHandlerDefs.IndexOfHandler(AClassName,AMethodName);
+end;
+
+function TJSONRPCHandlerManager.GetHandlerDef(Index: Integer
+  ): TJSONRPCHandlerDef;
+begin
+  Result:=FHandlerDefs[Index];
+end;
+
+function TJSONRPCHandlerManager.GetHandlerDefCount: Integer;
+begin
+  Result:=FHandlerDefs.Count;
+end;
+
+constructor TJSONRPCHandlerManager.Create(AOwner: TComponent);
+begin
+  inherited Create(AOwner);
+  FHandlerDefs:=TJSONRPCHandlerDefs.Create(TJSONRPCHandlerDef);
+end;
+
+destructor TJSONRPCHandlerManager.Destroy;
+begin
+  FreeAndNil(FHandlerDefs);
+  inherited Destroy;
+end;
+
+
+Initialization
+Finalization
+  FreeAndNil(TheHandler);
+end.
+

+ 189 - 0
packages/fcl-web/src/jsonrpc/readme.txt

@@ -0,0 +1,189 @@
+
+The JSON-RPC support for FPC consists out of 2 units:
+
+fpjsonrpc: a set of components suitable for executing requests.
+webjsonrpc: Handles all HTTP transport and content handling for the JSON-RPC requests.
+fpextdirect: implements a Ext.Direct variant of JSON-RPC; 
+             It includes HTTP transport an content handling.
+
+The fpjsonrpc unit implements support for JSON-RPC: it implements versions
+1.0 and 2.0 of the protocol, and has provisions for Ext.Direct-style RPC.
+
+It introduces the following classes:
+
+TCustomJSONRPCHandler = Class(TComponent)
+  The main method of this class is 
+    Function Execute(Const Params : TJSONData; AContext : TJSONRPCCallContext = Nil) : TJSONData;
+       it executes a JSON-RPC call. The parameters to the call are in the Params
+       parameter, the AContext parameter can be used by dispatching mechanisms to
+       provide information about the context of the call (HTTP session etc.)
+    Property ParamDefs : TJSONParamDefs Read FParamDefs Write SetParamDefs;
+       Describes the parameters to the call. If 'Options' contains jroCheckParams, 
+       then the types (and names, if appropriate) of the parameters will be checked.
+
+TJSONRPCHandler = Class(TCustomJSONRPCHandler)
+  A descendent which delegates the execution of the RPC call to an Event Handler.
+  Typically, one will drop an instance of TJSONRPCHandler on a webmodule.
+  Note that it does not provide context information in the event handler.
+
+TCustomJSONRPCDispatcher = Class(TComponent)
+  A request dispatching mechanism. It receives a JSON-encoded RPC request or a batch of
+  requests: 
+    Function Execute(Requests : TJSONData; AContext : TJSONRPCCallContext = Nil) : TJSONData;
+  For each request in the batch, it will examine the 'method' and 'class' 
+  of the request and will search for a TCustomJSONRPCHandler instance that
+  can execute the call, and pass the call on to that instance. It also
+  passes the context to the handler.
+
+  If jsoSearchOwner is in OPtions, then it will look on the Owner (usually a
+  TDatamodule descendent) for a TCustomJSONRPCHandler instance whose name
+  matches 'method' in the request, and will invoke its 'Execute' method.
+
+  if jdoSearchRegistry is in Options, then the JSONRPCHandlerManager factory
+  is queryied for an instance of TCustomJSONRPCHandler that can handle the
+  call. This instance is freed after the call ends.
+
+  The dispatcher can check the validity of the request and responses 
+  based on the Options property:
+    jdoJSONRPC1, // Allow JSON RPC-1
+    jdoJSONRPC2, // Allow JSON RPC-2
+    jdoRequireClass, // Require class name (as in  Ext.Direct)
+    jdoNotifications, // Allow JSON Notifications
+    jdoStrictNotifications // Error if notification returned result. Default is to discard result.
+
+TJSONRPCDispatcher = Class(TCustomJSONRPCDispatcher)
+  A descendent of TCustomJSONRPCDispatcher that publishes some events and  
+  properties:
+
+  OnStartBatch : Called before a batch is started
+  OnFindHandler : Called when no handler is found, can be used to construct custom method handlers.
+  OnDispatchRequest : Called before the request is dispatched to the handler.
+  OnEndBatch : called after the batch has been processed.
+
+  Most methods of this component can be overridden to provide customized
+  behaviour.
+
+
+TCustomJSONRPCHandlerManager = Class(TComponent)
+
+  A class that implements a TCustomJSONRPCHandler factory: definitions of TCustomJSONRPCHandler classes are kept.
+  classes must be registered using one of the calls:
+    Function RegisterHandler(Const AMethodName : TJSONStringType; AClass : TCustomJSONRPCHandlerClass; AArgumentCount : Integer = 0) : TJSONRPCHandlerDef; 
+    Function RegisterHandler(Const AClassName,AMethodName : TJSONStringType; AClass : TCustomJSONRPCHandlerClass; AArgumentCount : Integer = 0) :TJSONRPCHandlerDef; 
+
+  There is also support for registering a datamodule with TCustomJSONRPCHandler instances:
+
+    Procedure RegisterDatamodule(Const AClass : TDatamoduleClass; Const AHandlerClassName : TJSONStringType);
+
+  This will create an instance of the datamodule, register all TCustomJSONRPCHandler instances on the datamodule
+  with the datamodule name as the classname is AHandlerClassName is empty, and the TCustomJSONRPCHandler instance 
+  name as the method name. After registration, the module is freed. 
+
+A global instance of TCustomJSONRPCHandlerManager is available through the
+
+  Function JSONRPCHandlerManager : TCustomJSONRPCHandlerManager;
+
+function. By default, an instance of TJSONRPCHandlerManager is created.
+
+This instance is used by the dispatcher classes to search for a TCustomJSONRPCHandler instance.
+
+The webjsonrpc unit implements HTTP transport and content handling for the json-rpc mechanism. 
+It introduces the following classes:
+
+TCustomJSONRPCContentProducer = Class(THTTPContentProducer)    
+
+  Handles a HTTP request, extracts the JSON-RPC request from it, and returns the result in the HTTP response.
+  It needs a dispatcher instance, which must be provided by a descendent.
+
+TJSONRPCContentProducer = Class(TCustomJSONRPCContentProducer)
+  Publishes a Dispatcher property, which must be set by the programmer. 
+  All requests will be dispatched to this instance
+
+TJSONRPCSessionContext = Class(TJSONRPCCallContext) 
+  JSON-RPC call context which gives access to the HTTP Session object.
+
+TSessionJSONRPCDispatcher = Class(TCustomJSONRPCDispatcher)
+  JSON-RPC dispatcher which initializes the call context if the handler is
+  located on a websession module.
+
+TJSONRPCDispatchModule = Class(TSessionHTTPModule)
+  Webmodule which knows how to dispatch a request and create session information for it.
+
+TCustomJSONRPCModule = Class(TJSONRPCDispatchModule)
+  It completely handles a JSON-RPC request over HTTP. 
+  It creates a dispatcher if needed to handle the request.
+  The dispatcher is by default of type TSessionJSONRPCDispatcher
+
+TJSONRPCModule= Class(TCustomJSONRPCModule)  
+  A webmodule which can be registered in the fcl-web module registry. 
+  Publishes a property Dispatcher to dispatch a request, if set, this
+  dispatcher will be used.
+  Publishes a property DispatchOptions; If the module creates a dispatcher,  
+  it will be passed these properties.
+
+The fpextdirect unit contains the following classes:
+
+TCustomExtDirectDispatcher = Class(TCustomJSONRPCDispatcher)
+  A JSON-RPC dispatcher that conforms to Ext.Direct specifications.
+  It sets the default Options to require a class name.
+  It handles HTTP sessions just as TSessionJSONRPCDispatcher does.
+
+TExtDirectDispatcher = Class(TCustomExtDirectDispatcher)
+  Simply publishes all properties.
+
+TCustomExtDirectContentProducer = Class(TCustomJSONRPCContentProducer)
+  A content producer that knows how to handle the 'API' request. It
+  assumes the path is something like /myapp/extdirect/api. All other
+  paths are treated as actual method calls.
+
+TCustomExtDirectModule =  Class(TJSONRPCDispatchModule)
+  Handles JSON-RPC requests. It has 2 properties APIPath and RouterPath
+  which determine how the request is handled:
+  /modulepath/APIPath will create the API response.
+  /modulepath/RouterPath will route the JSON-RPC request.
+
+TExtDirectModule = Class(TCustomExtDirectModule)
+  Publishes the properties introduced in TCustomExtDirectModule.
+
+There are 4 ways to handle JSON-RPC in fcl-web:
+1. Manual:
+  A. Drop one or more TJSONRPCHandler components on a webmodule.
+  B. Implement the OnExecute handlers of the TJSONRPCHandler components
+  C. Handle the request in a OnRequest handler of an webaction and/or webmodule.
+  This means 
+  1. converting the request to JSONData.
+  2. Extracting the method(s) and (their) parameters from the data.
+  3. locating The TJSONRPCHandler.
+  4. Passing the parameters to the Execute method of the handler.
+  5. Convert the result to JSON and send it back.
+
+2. Using a TJSONRPCDispatcher:
+  A. Drop one or more TJSONRPCHandler components on a webmodule.
+  B. Implement the OnExecute handlers of the TJSONRPCHandler components
+  C. Handle the request in a OnRequest handler of an webaction and/or webmodule.
+  This means 
+  1. converting the request to JSONData.
+  4. Passing the parameters to the Execute method of the handler
+  5. Convert the result to JSON and send it back.
+
+3. Using a TJSONRPCContentProducer:
+   A. Drop one or more TJSONRPCHandler components on a webmodule.
+   B. Implement the OnExecute handlers of the TJSONRPCHandler components.
+   C. Handle the request in a OnRequest handler of an webaction and/or webmodule.
+   1. Return the content of the TJSONRPCContentProducer. 
+
+4. Using a TJSONRPCModule
+   A. Drop one or more TJSONRPCHandler on a TJSONRPCModule.
+   B. Implement the OnExecute handlers of the TJSONRPCHandler components.
+
+For large applicaions, TJSONRPCHandler instances can be dropped on
+datamodules or TJSONRPCModules, and the datamodule and/or TJSONRPCModules
+must be registered using
+  JSONRpcHandlerManager.RegisterModule(TMyModule,'myclassname');
+if TJSONRPCModules are used, only 1 must be registered as a webmodule
+to handle the request. It will dispatch the request to the other instances
+if needed.
+
+Note: if a request contains no class name, then the handler is searched
+using the first matching method name. If 2 modules implement the same
+method name, then the first match is used.

+ 316 - 0
packages/fcl-web/src/jsonrpc/webjsonrpc.pp

@@ -0,0 +1,316 @@
+unit webjsonrpc;
+
+{$mode objfpc}{$H+}
+{ $define debugjsonrpc}
+
+interface
+
+uses
+  Classes, SysUtils, fpjson, fpjsonrpc, httpdefs, fphttp, jsonparser, websession;
+
+Type
+{ ---------------------------------------------------------------------
+  HTTP handling and content producing methods
+  ---------------------------------------------------------------------}
+
+  { TCustomJSONRPCContentProducer }
+
+  TCustomJSONRPCContentProducer = Class(THTTPContentProducer)
+  Protected
+    Function GetIDProperty : String; virtual;
+    Procedure DoGetContent(ARequest : TRequest; Content : TStream; Var Handled : Boolean); override;
+    Function GetDispatcher : TCustomJSONRPCDispatcher; virtual; abstract;
+  end;
+
+  { TJSONRPCContentProducer }
+
+  TJSONRPCContentProducer = Class(TCustomJSONRPCContentProducer)
+  private
+    FDispatcher: TCustomJSONRPCDispatcher;
+    procedure SetDispatcher(const AValue: TCustomJSONRPCDispatcher);
+  Protected
+    Function GetDispatcher : TCustomJSONRPCDispatcher; override;
+    procedure Notification(AComponent: TComponent; Operation: TOperation);override;
+  Published
+    Property Dispatcher :  TCustomJSONRPCDispatcher Read FDispatcher Write SetDispatcher;
+  end;
+
+
+  { TJSONRPCSessionContext }
+
+  TJSONRPCSessionContext = Class(TJSONRPCCallContext)
+  private
+    FSession: TCustomSession;
+  Public
+    Constructor CreateSession(ASession : TCustomSession);
+    Property Session : TCustomSession Read FSession;
+  end;
+
+  { TSessionJSONRPCDispatcher }
+
+  TSessionJSONRPCDispatcher = Class(TCustomJSONRPCDispatcher)
+  Protected
+    Function FindHandler(Const AClassName,AMethodName : TJSONStringType;AContext : TJSONRPCCallContext; Out FreeObject : TComponent) : TCustomJSONRPCHandler; override;
+  Published
+    Property OnStartBatch;
+    Property OnDispatchRequest;
+    Property OnFindHandler;
+    Property OnEndBatch;
+    Property Options;
+  end;
+
+  { TJSONRPCDispatchModule }
+
+  TJSONRPCDispatchModule = Class(TSessionHTTPModule)
+  protected
+    Function CreateContext : TJSONRPCSessionContext;
+    Function DispatchRequest(Const ARequest : TRequest; ADispatcher : TCustomJSONRPCDispatcher) : TJSONData;
+  end;
+
+  { TCustomJSONRPCModule }
+
+  TCustomJSONRPCModule = Class(TJSONRPCDispatchModule)
+  private
+    FDispatcher: TCustomJSONRPCDispatcher;
+    FOptions: TJSONRPCDispatchOptions;
+    FRequest: TRequest;
+    FResponse: TResponse;
+    procedure SetDispatcher(const AValue: TCustomJSONRPCDispatcher);
+  Protected
+    Function CreateDispatcher : TCustomJSONRPCDispatcher; virtual;
+    procedure Notification(AComponent: TComponent; Operation: TOperation);override;
+    Property Dispatcher :  TCustomJSONRPCDispatcher Read FDispatcher Write SetDispatcher;
+    Property DispatchOptions : TJSONRPCDispatchOptions Read FOptions Write FOptions default DefaultDispatchOptions;
+  Public
+    Constructor CreateNew(AOwner : TComponent; CreateMode : Integer); override;
+    Procedure HandleRequest(ARequest : TRequest; AResponse : TResponse); override;
+    // Access to request
+    Property Request: TRequest Read FRequest;
+    // Access to response
+    Property Response: TResponse Read FResponse;
+  end;
+
+  { TJSONRPCDataModule }
+
+  TJSONRPCModule = Class(TCustomJSONRPCModule)
+  Published
+    Property Dispatcher;
+    Property DispatchOptions;
+  end;
+
+implementation
+
+{$ifdef debugjsonrpc}
+uses dbugintf;
+{$endif}
+
+{ TCustomJSONRPCContentProducer }
+
+function TCustomJSONRPCContentProducer.GetIDProperty: String;
+begin
+  Result:='id';
+end;
+
+
+procedure TCustomJSONRPCContentProducer.DoGetContent(ARequest: TRequest;
+  Content: TStream; var Handled: Boolean);
+
+Var
+  Disp : TCustomJSONRPCDispatcher;
+  P : TJSONParser;
+  Req,res : TJSONData;
+  R : String;
+
+begin
+  Disp:=Self.GetDispatcher;
+  P:= TJSONParser.Create(ARequest.Content);
+  try
+    Res:=Nil;
+    Req:=Nil;
+    try
+      try
+        Req:=P.Parse;
+        If (Disp<>Nil) then
+          Res:=Disp.Execute(Req,Nil)
+        else // No dispatcher, create error(s)
+          Res:=CreateErrorForRequest(Req,CreateJSON2ErrorResponse(SErrNoDispatcher,EJSONRPCInternalError,Nil,GetIDProperty));
+      except
+        On E : Exception Do
+          begin
+          Res:=CreateJSON2ErrorResponse(E.Message,EJSONRPCParseError,Nil,GetIDProperty);
+          end;
+      end;
+      try
+        If Assigned(Res) then
+          begin
+          R:=Res.AsJSON;
+          Content.WriteBuffer(R[1],Length(R));
+          end;
+        Handled:=True;
+      finally
+        FreeAndNil(Res);
+      end;
+    finally
+      Req.Free;
+    end;
+  finally
+    P.Free;
+  end;
+end;
+
+{ TJSONRPCContentProducer }
+
+procedure TJSONRPCContentProducer.SetDispatcher(
+  const AValue: TCustomJSONRPCDispatcher);
+begin
+  if FDispatcher=AValue then exit;
+  If Assigned(FDispatcher) then
+    FDispatcher.RemoveFreeNotification(Self);
+  FDispatcher:=AValue;
+  If Assigned(FDispatcher) then
+    FDispatcher.FreeNotification(Self);
+end;
+
+function TJSONRPCContentProducer.GetDispatcher: TCustomJSONRPCDispatcher;
+begin
+  Result:=FDispatcher;
+end;
+
+procedure TJSONRPCContentProducer.Notification(AComponent: TComponent;
+  Operation: TOperation);
+begin
+  inherited Notification(AComponent, Operation);
+  If (Operation=opRemove) and (AComponent=FDispatcher) then
+    FDispatcher:=Nil;
+end;
+
+{ TCustomJSONRPCModule }
+
+procedure TCustomJSONRPCModule.SetDispatcher(
+  const AValue: TCustomJSONRPCDispatcher);
+begin
+  if FDispatcher=AValue then exit;
+  If Assigned(FDispatcher) then
+    FDispatcher.RemoveFreeNotification(Self);
+  FDispatcher:=AValue;
+  If Assigned(FDispatcher) then
+    FDispatcher.FreeNotification(Self);
+end;
+
+function TCustomJSONRPCModule.CreateDispatcher: TCustomJSONRPCDispatcher;
+
+Var
+  S : TSessionJSONRPCDispatcher;
+
+begin
+  S:=TSessionJSONRPCDispatcher.Create(Self);
+  S.Options:=DispatchOptions;
+  Result:=S;
+end;
+
+procedure TCustomJSONRPCModule.Notification(AComponent: TComponent;
+  Operation: TOperation);
+begin
+  inherited Notification(AComponent, Operation);
+  If (Operation=opRemove) and (AComponent=FDispatcher) then
+    FDispatcher:=Nil;
+end;
+
+constructor TCustomJSONRPCModule.CreateNew(AOwner: TComponent;
+  CreateMode: Integer);
+begin
+  inherited CreateNew(AOwner, CreateMode);
+  FOptions:=DefaultDispatchOptions+[jdoSearchRegistry];
+end;
+
+
+
+procedure TCustomJSONRPCModule.HandleRequest(ARequest: TRequest;
+  AResponse: TResponse);
+
+Var
+  Disp : TCustomJSONRPCDispatcher;
+  res : TJSONData;
+
+begin
+  If (Dispatcher=Nil) then
+    Dispatcher:=CreateDispatcher;
+  Disp:=Dispatcher;
+  Res:=DispatchRequest(ARequest,Disp);
+  try
+    If Assigned(Res) then
+      begin
+      AResponse.Content:=Res.AsJSON;
+      AResponse.ContentLength:=Length(AResponse.Content);
+      end;
+  AResponse.SendResponse;
+  finally
+    Res.Free;
+  end;
+end;
+
+{ TJSONRPCSessionContext }
+
+constructor TJSONRPCSessionContext.CreateSession(ASession: TCustomSession);
+begin
+  FSession:=ASession;
+end;
+
+{ TJSONRPCDispatchModule }
+
+function TJSONRPCDispatchModule.CreateContext: TJSONRPCSessionContext;
+begin
+  If CreateSession then
+    Result:=TJSONRPCSessionContext.CreateSession(Session)
+  else
+    Result:=TJSONRPCSessionContext.CreateSession(Nil);
+end;
+
+Function TJSONRPCDispatchModule.DispatchRequest(const ARequest: TRequest;
+  ADispatcher: TCustomJSONRPCDispatcher): TJSONData;
+var
+  P : TJSONParser;
+  Req : TJSONData;
+  C : TJSONRPCSessionContext;
+
+
+begin
+  P:= TJSONParser.Create(ARequest.Content);
+  try
+    Result:=Nil;
+    Req:=Nil;
+    try
+      try
+        Req:=P.Parse;
+        C:=CreateContext;
+        try
+         {$ifdef debugjsonrpc}SendDebugFmt('Dispatching request : "%s"',[Req.AsJSON]);{$endif}
+          Result:=ADispatcher.Execute(Req,C);
+        finally
+          C.Free;
+        end;
+      except
+        On E : Exception Do
+          Result:=CreateJSON2ErrorResponse(E.Message,EJSONRPCParseError,Nil,ADispatcher.TransactionProperty);
+      end;
+    finally
+      Req.Free;
+    end;
+  finally
+    P.Free;
+  end;
+end;
+
+{ TSessionJSONRPCDispatcher }
+
+function TSessionJSONRPCDispatcher.FindHandler(const AClassName,
+  AMethodName: TJSONStringType; AContext: TJSONRPCCallContext; out
+  FreeObject: TComponent): TCustomJSONRPCHandler;
+begin
+  Result:=Inherited FindHandler(AClassName,AMethodName,AContext,FreeObject);
+  If (AContext is TJSONRPCSessionContext) and (FreeObject is TCustomJSONRPCModule) then
+    TCustomJSONRPCModule(FreeObject).Session:=TJSONRPCSessionContext(AContext).Session;
+end;
+
+end.
+