Pārlūkot izejas kodu

* basic framework for actions

git-svn-id: trunk@6306 -
peter 18 gadi atpakaļ
vecāks
revīzija
f2747f1e40

+ 3 - 0
.gitattributes

@@ -8204,6 +8204,7 @@ utils/fpmc/test.mc -text
 utils/fppkg/Makefile svneol=native#text/plain
 utils/fppkg/Makefile.fpc svneol=native#text/plain
 utils/fppkg/README svneol=native#text/plain
+utils/fppkg/contnrs20.pp svneol=native#text/plain
 utils/fppkg/fpmkcnst.inc svneol=native#text/plain
 utils/fppkg/fpmktype.pp svneol=native#text/plain
 utils/fppkg/fpmkunit.pp svneol=native#text/plain
@@ -8240,6 +8241,7 @@ utils/fppkg/lnet/sys/lspawnfcgiunix.inc svneol=native#text/plain
 utils/fppkg/lnet/sys/lspawnfcgiwin.inc svneol=native#text/plain
 utils/fppkg/lnet/sys/osunits.inc svneol=native#text/plain
 utils/fppkg/pkgdownload.pp svneol=native#text/plain
+utils/fppkg/pkgfpmake.pp svneol=native#text/plain
 utils/fppkg/pkghandler.pp svneol=native#text/plain
 utils/fppkg/pkglibcurl.pp svneol=native#text/plain
 utils/fppkg/pkglnet.pas svneol=native#text/plain
@@ -8252,6 +8254,7 @@ utils/fppkg/pkgwget.pp svneol=native#text/plain
 utils/fppkg/rep2xml.lpi svneol=native#text/plain
 utils/fppkg/rep2xml.lpr svneol=native#text/plain
 utils/fppkg/reptest.pp svneol=native#text/plain
+utils/fppkg/streamcoll20.pp svneol=native#text/plain
 utils/fppkg/testdownload.pp svneol=native#text/plain
 utils/fprcp/Makefile svneol=native#text/plain
 utils/fprcp/Makefile.fpc svneol=native#text/plain

+ 2344 - 0
utils/fppkg/contnrs20.pp

@@ -0,0 +1,2344 @@
+{
+    This file is part of the Free Component Library (FCL)
+    Copyright (c) 2002 by Florian Klaempfl
+
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+{$ifdef fpc}
+{$mode objfpc}
+{$endif}
+{$H+}
+{$ifdef CLASSESINLINE}{$inline on}{$endif}
+
+unit contnrs20;
+
+interface
+
+uses
+  SysUtils,Classes;
+
+
+Type
+  TObjectListCallback = procedure(data:TObject;arg:pointer) of object;
+  TObjectListStaticCallback = procedure(data:TObject;arg:pointer);
+
+  TFPObjectList = class(TObject)
+  private
+    FFreeObjects : Boolean;
+    FList: TFPList;
+    function GetCount: integer;
+    procedure SetCount(const AValue: integer);
+  protected
+    function GetItem(Index: Integer): TObject; {$ifdef CLASSESINLINE}inline;{$endif}
+    procedure SetItem(Index: Integer; AObject: TObject); {$ifdef CLASSESINLINE}inline;{$endif}
+    procedure SetCapacity(NewCapacity: Integer);
+    function GetCapacity: integer;
+  public
+    constructor Create;
+    constructor Create(FreeObjects : Boolean);
+    destructor Destroy; override;
+    procedure Clear;
+    function Add(AObject: TObject): Integer; {$ifdef CLASSESINLINE}inline;{$endif}
+    procedure Delete(Index: Integer); {$ifdef CLASSESINLINE}inline;{$endif}
+    procedure Exchange(Index1, Index2: Integer);
+    function Expand: TFPObjectList;
+    function Extract(Item: TObject): TObject;
+    function Remove(AObject: TObject): Integer;
+    function IndexOf(AObject: TObject): Integer;
+    function FindInstanceOf(AClass: TClass; AExact: Boolean; AStartAt: Integer): Integer;
+    procedure Insert(Index: Integer; AObject: TObject); {$ifdef CLASSESINLINE}inline;{$endif}
+    function First: TObject;
+    function Last: TObject;
+    procedure Move(CurIndex, NewIndex: Integer);
+    procedure Assign(Obj:TFPObjectList);
+    procedure Pack;
+    procedure Sort(Compare: TListSortCompare);
+    procedure ForEachCall(proc2call:TObjectListCallback;arg:pointer);
+    procedure ForEachCall(proc2call:TObjectListStaticCallback;arg:pointer);
+    property Capacity: Integer read GetCapacity write SetCapacity;
+    property Count: Integer read GetCount write SetCount;
+    property OwnsObjects: Boolean read FFreeObjects write FFreeObjects;
+    property Items[Index: Integer]: TObject read GetItem write SetItem; default;
+    property List: TFPList read FList;
+  end;
+
+
+  TObjectList = class(TList)
+  private
+    ffreeobjects : boolean;
+  Protected
+    Procedure Notify(Ptr: Pointer; Action: TListNotification); override;
+    function GetItem(Index: Integer): TObject;
+    Procedure SetItem(Index: Integer; AObject: TObject);
+  public
+    constructor create;
+    constructor create(freeobjects : boolean);
+    function Add(AObject: TObject): Integer;
+    function Extract(Item: TObject): TObject;
+    function Remove(AObject: TObject): Integer;
+    function IndexOf(AObject: TObject): Integer;
+    function FindInstanceOf(AClass: TClass; AExact: Boolean; AStartAt: Integer): Integer;
+    Procedure Insert(Index: Integer; AObject: TObject);
+    function First: TObject;
+    Function Last: TObject;
+    property OwnsObjects: Boolean read FFreeObjects write FFreeObjects;
+    property Items[Index: Integer]: TObject read GetItem write SetItem; default;
+  end;
+
+  TComponentList = class(TObjectList)
+  Private
+    FNotifier : TComponent;
+  Protected
+    Procedure Notify(Ptr: Pointer; Action: TListNotification); override;
+    Function GetItems(Index: Integer): TComponent;
+    Procedure SetItems(Index: Integer; AComponent: TComponent);
+    Procedure HandleFreeNotify(Sender: TObject; AComponent: TComponent);
+  public
+    destructor Destroy; override;
+    Function Add(AComponent: TComponent): Integer;
+    Function Extract(Item: TComponent): TComponent;
+    Function Remove(AComponent: TComponent): Integer;
+    Function IndexOf(AComponent: TComponent): Integer;
+    Function First: TComponent;
+    Function Last: TComponent;
+    Procedure Insert(Index: Integer; AComponent: TComponent);
+    property Items[Index: Integer]: TComponent read GetItems write SetItems; default;
+  end;
+
+  TClassList = class(TList)
+  protected
+    Function GetItems(Index: Integer): TClass;
+    Procedure SetItems(Index: Integer; AClass: TClass);
+  public
+    Function Add(AClass: TClass): Integer;
+    Function Extract(Item: TClass): TClass;
+    Function Remove(AClass: TClass): Integer;
+    Function IndexOf(AClass: TClass): Integer;
+    Function First: TClass;
+    Function Last: TClass;
+    Procedure Insert(Index: Integer; AClass: TClass);
+    property Items[Index: Integer]: TClass read GetItems write SetItems; default;
+  end;
+
+  TOrderedList = class(TObject)
+  private
+    FList: TList;
+  protected
+    Procedure PushItem(AItem: Pointer); virtual; abstract;
+    Function PopItem: Pointer; virtual;
+    Function PeekItem: Pointer; virtual;
+    property List: TList read FList;
+  public
+    constructor Create;
+    destructor Destroy; override;
+    Function Count: Integer;
+    Function AtLeast(ACount: Integer): Boolean;
+    Function Push(AItem: Pointer): Pointer;
+    Function Pop: Pointer;
+    Function Peek: Pointer;
+  end;
+
+{ TStack class }
+
+  TStack = class(TOrderedList)
+  protected
+    Procedure PushItem(AItem: Pointer); override;
+  end;
+
+{ TObjectStack class }
+
+  TObjectStack = class(TStack)
+  public
+    Function Push(AObject: TObject): TObject;
+    Function Pop: TObject;
+    Function Peek: TObject;
+  end;
+
+{ TQueue class }
+
+  TQueue = class(TOrderedList)
+  protected
+    Procedure PushItem(AItem: Pointer); override;
+  end;
+
+{ TObjectQueue class }
+
+  TObjectQueue = class(TQueue)
+  public
+    Function Push(AObject: TObject): TObject;
+    Function Pop: TObject;
+    Function Peek: TObject;
+  end;
+
+{ ---------------------------------------------------------------------
+    TFPList with Hash support
+  ---------------------------------------------------------------------}
+
+type
+  THashItem=record
+    HashValue : LongWord;
+    StrIndex  : Integer;
+    NextIndex : Integer;
+    Data      : Pointer;
+  end;
+  PHashItem=^THashItem;
+
+const
+  MaxHashListSize = Maxint div 16;
+  MaxHashStrSize  = Maxint;
+  MaxHashTableSize = Maxint div 4;
+  MaxItemsPerHash = 3;
+
+type
+  PHashItemList = ^THashItemList;
+  THashItemList = array[0..MaxHashListSize - 1] of THashItem;
+  PHashTable = ^THashTable;
+  THashTable = array[0..MaxHashTableSize - 1] of Integer;
+
+  TFPHashList = class(TObject)
+  private
+    { ItemList }
+    FHashList     : PHashItemList;
+    FCount,
+    FCapacity : Integer;
+    { Hash }
+    FHashTable    : PHashTable;
+    FHashCapacity : Integer;
+    { Strings }
+    FStrs     : PChar;
+    FStrCount,
+    FStrCapacity : Integer;
+    function InternalFind(AHash:LongWord;const AName:shortstring;out PrevIndex:Integer):Integer;
+  protected
+    function Get(Index: Integer): Pointer; {$ifdef CCLASSESINLINE}inline;{$endif}
+    procedure Put(Index: Integer; Item: Pointer); {$ifdef CCLASSESINLINE}inline;{$endif}
+    procedure SetCapacity(NewCapacity: Integer);
+    procedure SetCount(NewCount: Integer);
+    Procedure RaiseIndexError(Index : Integer);
+    function  AddStr(const s:shortstring): Integer;
+    procedure AddToHashTable(Index: Integer);
+    procedure StrExpand(MinIncSize:Integer);
+    procedure SetStrCapacity(NewCapacity: Integer);
+    procedure SetHashCapacity(NewCapacity: Integer);
+    procedure ReHash;
+  public
+    constructor Create;
+    destructor Destroy; override;
+    function Add(const AName:shortstring;Item: Pointer): Integer;
+    procedure Clear;
+    function NameOfIndex(Index: Integer): ShortString; {$ifdef CCLASSESINLINE}inline;{$endif}
+    function HashOfIndex(Index: Integer): LongWord; {$ifdef CCLASSESINLINE}inline;{$endif}
+    procedure Delete(Index: Integer);
+    class procedure Error(const Msg: string; Data: PtrInt);
+    function Expand: TFPHashList;
+    function Extract(item: Pointer): Pointer;
+    function IndexOf(Item: Pointer): Integer;
+    function Find(const AName:shortstring): Pointer;
+    function FindIndexOf(const AName:shortstring): Integer;
+    function FindWithHash(const AName:shortstring;AHash:LongWord): Pointer;
+    function Rename(const AOldName,ANewName:shortstring): Integer;
+    function Remove(Item: Pointer): Integer;
+    procedure Pack;
+    procedure ShowStatistics;
+    procedure ForEachCall(proc2call:TListCallback;arg:pointer);
+    procedure ForEachCall(proc2call:TListStaticCallback;arg:pointer);
+    property Capacity: Integer read FCapacity write SetCapacity;
+    property Count: Integer read FCount write SetCount;
+    property Items[Index: Integer]: Pointer read Get write Put; default;
+    property List: PHashItemList read FHashList;
+    property Strs: PChar read FStrs;
+  end;
+
+
+{*******************************************************
+        TFPHashObjectList (From fcl/inc/contnrs.pp)
+********************************************************}
+
+  TFPHashObjectList = class;
+
+  { TFPHashObject }
+
+  TFPHashObject = class
+  private
+    FOwner     : TFPHashObjectList;
+    FCachedStr : pshortstring;
+    FStrIndex  : Integer;
+    procedure InternalChangeOwner(HashObjectList:TFPHashObjectList;const s:shortstring);
+  protected
+    function GetName:shortstring;virtual;
+    function GetHash:Longword;virtual;
+  public
+    constructor CreateNotOwned;
+    constructor Create(HashObjectList:TFPHashObjectList;const s:shortstring);
+    procedure ChangeOwner(HashObjectList:TFPHashObjectList); {$ifdef CCLASSESINLINE}inline;{$endif}
+    procedure ChangeOwnerAndName(HashObjectList:TFPHashObjectList;const s:shortstring); {$ifdef CCLASSESINLINE}inline;{$endif}
+    procedure Rename(const ANewName:shortstring);
+    property Name:shortstring read GetName;
+    property Hash:Longword read GetHash;
+  end;
+
+  TFPHashObjectList = class(TObject)
+  private
+    FFreeObjects : Boolean;
+    FHashList: TFPHashList;
+    function GetCount: integer; {$ifdef CCLASSESINLINE}inline;{$endif}
+    procedure SetCount(const AValue: integer); {$ifdef CCLASSESINLINE}inline;{$endif}
+  protected
+    function GetItem(Index: Integer): TObject; {$ifdef CCLASSESINLINE}inline;{$endif}
+    procedure SetItem(Index: Integer; AObject: TObject); {$ifdef CCLASSESINLINE}inline;{$endif}
+    procedure SetCapacity(NewCapacity: Integer); {$ifdef CCLASSESINLINE}inline;{$endif}
+    function GetCapacity: integer; {$ifdef CCLASSESINLINE}inline;{$endif}
+  public
+    constructor Create(FreeObjects : boolean = True);
+    destructor Destroy; override;
+    procedure Clear;
+    function Add(const AName:shortstring;AObject: TObject): Integer; {$ifdef CCLASSESINLINE}inline;{$endif}
+    function NameOfIndex(Index: Integer): ShortString; {$ifdef CCLASSESINLINE}inline;{$endif}
+    function HashOfIndex(Index: Integer): LongWord; {$ifdef CCLASSESINLINE}inline;{$endif}
+    procedure Delete(Index: Integer);
+    function Expand: TFPHashObjectList; {$ifdef CCLASSESINLINE}inline;{$endif}
+    function Extract(Item: TObject): TObject; {$ifdef CCLASSESINLINE}inline;{$endif}
+    function Remove(AObject: TObject): Integer;
+    function IndexOf(AObject: TObject): Integer; {$ifdef CCLASSESINLINE}inline;{$endif}
+    function Find(const s:shortstring): TObject; {$ifdef CCLASSESINLINE}inline;{$endif}
+    function FindIndexOf(const s:shortstring): Integer; {$ifdef CCLASSESINLINE}inline;{$endif}
+    function FindWithHash(const AName:shortstring;AHash:LongWord): Pointer;
+    function Rename(const AOldName,ANewName:shortstring): Integer; {$ifdef CCLASSESINLINE}inline;{$endif}
+    function FindInstanceOf(AClass: TClass; AExact: Boolean; AStartAt: Integer): Integer;
+    procedure Pack; {$ifdef CCLASSESINLINE}inline;{$endif}
+    procedure ShowStatistics; {$ifdef CCLASSESINLINE}inline;{$endif}
+    procedure ForEachCall(proc2call:TObjectListCallback;arg:pointer); {$ifdef CCLASSESINLINE}inline;{$endif}
+    procedure ForEachCall(proc2call:TObjectListStaticCallback;arg:pointer); {$ifdef CCLASSESINLINE}inline;{$endif}
+    property Capacity: Integer read GetCapacity write SetCapacity;
+    property Count: Integer read GetCount write SetCount;
+    property OwnsObjects: Boolean read FFreeObjects write FFreeObjects;
+    property Items[Index: Integer]: TObject read GetItem write SetItem; default;
+    property List: TFPHashList read FHashList;
+  end;
+
+{ ---------------------------------------------------------------------
+    Hash support, implemented by Dean Zobec
+  ---------------------------------------------------------------------}
+
+
+  { Must return a Longword value in the range 0..TableSize,
+   usually via a mod operator;  }
+  THashFunction = function(const S: string; const TableSize: Longword): Longword;
+
+
+  { THTNode }
+
+  THTCustomNode = class(TObject)
+  private
+    FKey: string;
+  public
+    constructor CreateWith(const AString: String);
+    function HasKey(const AKey: string): boolean;
+    property Key: string read FKey;
+  end;
+  THTCustomNodeClass = Class of THTCustomNode;
+
+
+  { TFPCustomHashTable }
+
+  TFPCustomHashTable = class(TObject)
+  private
+    FHashTable: TFPObjectList;
+    FHashTableSize: Longword;
+    FHashFunction: THashFunction;
+    FCount: Longword;
+    function GetDensity: Longword;
+    function GetNumberOfCollisions: Longword;
+    procedure SetHashTableSize(const Value: Longword);
+    procedure InitializeHashTable;
+    function GetVoidSlots: Longword;
+    function GetLoadFactor: double;
+    function GetAVGChainLen: double;
+    function GetMaxChainLength: Longword;
+    function Chain(const index: Longword):TFPObjectList;
+  protected
+    Function CreateNewNode(const aKey : string) : THTCustomNode; virtual; abstract;
+    Procedure AddNode(ANode : THTCustomNode); virtual; abstract;
+    function ChainLength(const ChainIndex: Longword): Longword; virtual;
+    function FindOrCreateNew(const aKey: string): THTCustomNode; virtual;
+    procedure SetHashFunction(AHashFunction: THashFunction); virtual;
+    Function FindChainForAdd(Const aKey : String) : TFPObjectList;
+  public
+    constructor Create;
+    constructor CreateWith(AHashTableSize: Longword; aHashFunc: THashFunction);
+    destructor Destroy; override;
+    procedure ChangeTableSize(const ANewSize: Longword); virtual;
+    procedure Clear; virtual;
+    procedure Delete(const aKey: string); virtual;
+    function Find(const aKey: string): THTCustomNode;
+    function IsEmpty: boolean;
+    property HashFunction: THashFunction read FHashFunction write SetHashFunction;
+    property Count: Longword read FCount;
+    property HashTableSize: Longword read FHashTableSize write SetHashTableSize;
+    property HashTable: TFPObjectList read FHashTable;
+    property VoidSlots: Longword read GetVoidSlots;
+    property LoadFactor: double read GetLoadFactor;
+    property AVGChainLen: double read GetAVGChainLen;
+    property MaxChainLength: Longword read GetMaxChainLength;
+    property NumberOfCollisions: Longword read GetNumberOfCollisions;
+    property Density: Longword read GetDensity;
+  end;
+
+  { TFPDataHashTable : Hash table with simple data pointers }
+
+  THTDataNode = Class(THTCustomNode)
+  Private
+    FData: pointer;
+  public
+    property Data: pointer read FData write FData;
+  end;
+  // For compatibility
+  THTNode = THTDataNode;
+
+  TDataIteratorMethod = procedure(Item: Pointer; const Key: string; var Continue: Boolean) of object;
+  // For compatibility
+  TIteratorMethod = TDataIteratorMethod;
+
+  TFPDataHashTable = Class(TFPCustomHashTable)
+  Protected
+    Function CreateNewNode(const aKey : String) : THTCustomNode; override;
+    Procedure AddNode(ANode : THTCustomNode); override;
+    procedure SetData(const index: string; const AValue: Pointer); virtual;
+    function GetData(const index: string):Pointer; virtual;
+    function ForEachCall(aMethod: TDataIteratorMethod): THTDataNode; virtual;
+  Public
+    procedure Add(const aKey: string; AItem: pointer); virtual;
+    property Items[const index: string]: Pointer read GetData write SetData; default;
+  end;
+
+  { TFPStringHashTable : Hash table with simple strings as data }
+  THTStringNode = Class(THTCustomNode)
+  Private
+    FData : String;
+  public
+    property Data: String read FData write FData;
+  end;
+  TStringIteratorMethod = procedure(Item: String; const Key: string; var Continue: Boolean) of object;
+
+  TFPStringHashTable = Class(TFPCustomHashTable)
+  Protected
+    Function CreateNewNode(const aKey : String) : THTCustomNode; override;
+    Procedure AddNode(ANode : THTCustomNode); override;
+    procedure SetData(const Index, AValue: string); virtual;
+    function GetData(const index: string): String; virtual;
+    function ForEachCall(aMethod: TStringIteratorMethod): THTStringNode; virtual;
+  Public
+    procedure Add(const aKey,aItem: string); virtual;
+    property Items[const index: string]: String read GetData write SetData; default;
+  end;
+
+  { TFPStringHashTable : Hash table with simple strings as data }
+
+
+  THTObjectNode = Class(THTCustomNode)
+  Private
+    FData : TObject;
+  public
+    property Data: TObject read FData write FData;
+  end;
+
+  THTOwnedObjectNode = Class(THTObjectNode)
+  public
+    Destructor Destroy; override;
+  end;
+  TObjectIteratorMethod = procedure(Item: TObject; const Key: string; var Continue: Boolean) of object;
+
+  TFPObjectHashTable = Class(TFPCustomHashTable)
+  Private
+    FOwnsObjects : Boolean;
+  Protected
+    Function CreateNewNode(const aKey : String) : THTCustomNode; override;
+    Procedure AddNode(ANode : THTCustomNode); override;
+    procedure SetData(const Index: string; AObject : TObject); virtual;
+    function GetData(const index: string): TObject; virtual;
+    function ForEachCall(aMethod: TObjectIteratorMethod): THTObjectNode; virtual;
+  Public
+    constructor Create(AOwnsObjects : Boolean = True);
+    constructor CreateWith(AHashTableSize: Longword; aHashFunc: THashFunction; AOwnsObjects : Boolean = True);
+    procedure Add(const aKey: string; AItem : TObject); virtual;
+    property Items[const index: string]: TObject read GetData write SetData; default;
+    Property OwnsObjects : Boolean Read FOwnsObjects Write FOwnsObjects;
+  end;
+
+
+  EDuplicate = class(Exception);
+  EKeyNotFound = class(Exception);
+
+
+  function RSHash(const S: string; const TableSize: Longword): Longword;
+
+implementation
+
+uses
+  RtlConsts;
+
+ResourceString
+  DuplicateMsg = 'An item with key %0:s already exists';
+  KeyNotFoundMsg = 'Method: %0:s key [''%1:s''] not found in container';
+  NotEmptyMsg = 'Hash table not empty.';
+
+const
+  NPRIMES = 28;
+
+  PRIMELIST: array[0 .. NPRIMES-1] of Longword =
+  ( 53,         97,         193,       389,       769,
+    1543,       3079,       6151,      12289,     24593,
+    49157,      98317,      196613,    393241,    786433,
+    1572869,    3145739,    6291469,   12582917,  25165843,
+    50331653,   100663319,  201326611, 402653189, 805306457,
+    1610612741, 3221225473, 4294967291 );
+
+constructor TFPObjectList.Create(FreeObjects : boolean);
+begin
+  Create;
+  FFreeObjects := Freeobjects;
+end;
+
+destructor TFPObjectList.Destroy;
+begin
+  if (FList <> nil) then
+  begin
+    Clear;
+    FList.Destroy;
+  end;
+  inherited Destroy;
+end;
+
+procedure TFPObjectList.Clear;
+var
+  i: integer;
+begin
+  if FFreeObjects then
+    for i := 0 to FList.Count - 1 do
+      TObject(FList[i]).Free;
+  FList.Clear;
+end;
+
+constructor TFPObjectList.Create;
+begin
+  inherited Create;
+  FList := TFPList.Create;
+  FFreeObjects := True;
+end;
+
+function TFPObjectList.GetCount: integer;
+begin
+  Result := FList.Count;
+end;
+
+procedure TFPObjectList.SetCount(const AValue: integer);
+begin
+  if FList.Count <> AValue then
+    FList.Count := AValue;
+end;
+
+function TFPObjectList.GetItem(Index: Integer): TObject; {$ifdef CLASSESINLINE}inline;{$endif}
+begin
+  Result := TObject(FList[Index]);
+end;
+
+procedure TFPObjectList.SetItem(Index: Integer; AObject: TObject); {$ifdef CLASSESINLINE}inline;{$endif}
+begin
+  if OwnsObjects then
+    TObject(FList[Index]).Free;
+  FList[index] := AObject;
+end;
+
+procedure TFPObjectList.SetCapacity(NewCapacity: Integer);
+begin
+  FList.Capacity := NewCapacity;
+end;
+
+function TFPObjectList.GetCapacity: integer;
+begin
+  Result := FList.Capacity;
+end;
+
+function TFPObjectList.Add(AObject: TObject): Integer; {$ifdef CLASSESINLINE}inline;{$endif}
+begin
+  Result := FList.Add(AObject);
+end;
+
+procedure TFPObjectList.Delete(Index: Integer); {$ifdef CLASSESINLINE}inline;{$endif}
+begin
+  if OwnsObjects then
+    TObject(FList[Index]).Free;
+  FList.Delete(Index);
+end;
+
+procedure TFPObjectList.Exchange(Index1, Index2: Integer);
+begin
+  FList.Exchange(Index1, Index2);
+end;
+
+function TFPObjectList.Expand: TFPObjectList;
+begin
+  FList.Expand;
+  Result := Self;
+end;
+
+function TFPObjectList.Extract(Item: TObject): TObject;
+begin
+  Result := TObject(FList.Extract(Item));
+end;
+
+function TFPObjectList.Remove(AObject: TObject): Integer;
+begin
+  Result := IndexOf(AObject);
+  if (Result <> -1) then
+  begin
+    if OwnsObjects then
+      TObject(FList[Result]).Free;
+    FList.Delete(Result);
+  end;
+end;
+
+function TFPObjectList.IndexOf(AObject: TObject): Integer;
+begin
+  Result := FList.IndexOf(Pointer(AObject));
+end;
+
+function TFPObjectList.FindInstanceOf(AClass: TClass; AExact: Boolean; AStartAt : Integer): Integer;
+var
+  I : Integer;
+begin
+  I:=AStartAt;
+  Result:=-1;
+  If AExact then
+    while (I<Count) and (Result=-1) do
+      If Items[i].ClassType=AClass then
+        Result:=I
+      else
+        Inc(I)
+  else
+    while (I<Count) and (Result=-1) do
+      If Items[i].InheritsFrom(AClass) then
+        Result:=I
+      else
+        Inc(I);
+end;
+
+procedure TFPObjectList.Insert(Index: Integer; AObject: TObject); {$ifdef CLASSESINLINE}inline;{$endif}
+begin
+  FList.Insert(Index, Pointer(AObject));
+end;
+
+procedure TFPObjectList.Move(CurIndex, NewIndex: Integer);
+begin
+  FList.Move(CurIndex, NewIndex);
+end;
+
+procedure TFPObjectList.Assign(Obj: TFPObjectList);
+var
+  i: Integer;
+begin
+  Clear;
+  for I := 0 to Obj.Count - 1 do
+    Add(Obj[i]);
+end;
+
+procedure TFPObjectList.Pack;
+begin
+  FList.Pack;
+end;
+
+procedure TFPObjectList.Sort(Compare: TListSortCompare);
+begin
+  FList.Sort(Compare);
+end;
+
+function TFPObjectList.First: TObject;
+begin
+  Result := TObject(FList.First);
+end;
+
+function TFPObjectList.Last: TObject;
+begin
+  Result := TObject(FList.Last);
+end;
+
+procedure TFPObjectList.ForEachCall(proc2call:TObjectListCallback;arg:pointer);
+begin
+  FList.ForEachCall(TListCallBack(proc2call),arg);
+end;
+
+procedure TFPObjectList.ForEachCall(proc2call:TObjectListStaticCallback;arg:pointer);
+begin
+  FList.ForEachCall(TListStaticCallBack(proc2call),arg);
+end;
+
+
+{ TObjectList }
+
+constructor tobjectlist.create(freeobjects : boolean);
+
+begin
+  inherited create;
+  ffreeobjects:=freeobjects;
+end;
+
+Constructor tobjectlist.create;
+
+begin
+  inherited create;
+  ffreeobjects:=True;
+end;
+
+Procedure TObjectList.Notify(Ptr: Pointer; Action: TListNotification);
+
+begin
+  if FFreeObjects then
+    if (Action=lnDeleted) then
+      TObject(Ptr).Free;
+  inherited Notify(Ptr,Action);
+end;
+
+
+Function TObjectList.GetItem(Index: Integer): TObject;
+
+begin
+  Result:=TObject(Inherited Get(Index));
+end;
+
+
+Procedure TObjectList.SetItem(Index: Integer; AObject: TObject);
+
+Var
+  O : TObject;
+
+begin
+  if OwnsObjects then
+    begin
+    O:=GetItem(Index);
+    O.Free;
+    end;
+  Put(Index,Pointer(AObject));
+end;
+
+
+Function TObjectList.Add(AObject: TObject): Integer;
+
+begin
+  Result:=Inherited Add(Pointer(AObject));
+end;
+
+
+Function TObjectList.Extract(Item: TObject): TObject;
+
+begin
+  Result:=Tobject(Inherited Extract(Pointer(Item)));
+end;
+
+
+Function TObjectList.Remove(AObject: TObject): Integer;
+
+begin
+  Result:=Inherited Remove(Pointer(AObject));
+end;
+
+
+Function TObjectList.IndexOf(AObject: TObject): Integer;
+
+begin
+  Result:=Inherited indexOF(Pointer(AObject));
+end;
+
+
+Function TObjectList.FindInstanceOf(AClass: TClass; AExact: Boolean; AStartAt : Integer): Integer;
+
+Var
+  I : Integer;
+
+begin
+  I:=AStartAt;
+  Result:=-1;
+  If AExact then
+    While (I<Count) and (Result=-1) do
+      If Items[i].ClassType=AClass then
+        Result:=I
+      else
+        Inc(I)
+  else
+    While (I<Count) and (Result=-1) do
+      If Items[i].InheritsFrom(AClass) then
+        Result:=I
+      else
+        Inc(I);
+end;
+
+
+procedure TObjectList.Insert(Index: Integer; AObject: TObject);
+begin
+  Inherited Insert(Index,Pointer(AObject));
+end;
+
+
+function TObjectList.First: TObject;
+
+begin
+  Result := TObject(Inherited First);
+end;
+
+
+function TObjectList.Last: TObject;
+
+begin
+  Result := TObject(Inherited Last);
+end;
+
+{ TListComponent }
+
+Type
+  TlistComponent = Class(TComponent)
+  Private
+    Flist : TComponentList;
+  Public
+    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
+  end;
+
+procedure TlistComponent.Notification(AComponent: TComponent;
+  Operation: TOperation);
+begin
+  If (Operation=opremove) then
+    Flist.HandleFreeNotify(Self,AComponent);
+  inherited;
+end;
+
+{ TComponentList }
+
+Function TComponentList.Add(AComponent: TComponent): Integer;
+begin
+  Result:=Inherited Add(AComponent);
+end;
+
+destructor TComponentList.Destroy;
+begin
+  inherited;
+  FreeAndNil(FNotifier);
+end;
+
+Function TComponentList.Extract(Item: TComponent): TComponent;
+begin
+  Result:=TComponent(Inherited Extract(Item));
+end;
+
+Function TComponentList.First: TComponent;
+begin
+  Result:=TComponent(Inherited First);
+end;
+
+Function TComponentList.GetItems(Index: Integer): TComponent;
+begin
+  Result:=TComponent(Inherited Items[Index]);
+end;
+
+Procedure TComponentList.HandleFreeNotify(Sender: TObject;
+  AComponent: TComponent);
+begin
+  Extract(Acomponent);
+end;
+
+Function TComponentList.IndexOf(AComponent: TComponent): Integer;
+begin
+  Result:=Inherited IndexOf(AComponent);
+end;
+
+Procedure TComponentList.Insert(Index: Integer; AComponent: TComponent);
+begin
+  Inherited Insert(Index,Acomponent)
+end;
+
+Function TComponentList.Last: TComponent;
+begin
+  Result:=TComponent(Inherited Last);
+end;
+
+Procedure TComponentList.Notify(Ptr: Pointer; Action: TListNotification);
+begin
+  If FNotifier=NIl then
+    begin
+    FNotifier:=TlistComponent.Create(nil);
+    TlistComponent(FNotifier).FList:=Self;
+    end;
+  If Assigned(Ptr) then
+    With TComponent(Ptr) do
+      case Action of
+        lnAdded : FreeNotification(FNotifier);
+        lnExtracted, lnDeleted: RemoveFreeNotification(FNotifier);
+      end;
+  inherited Notify(Ptr, Action);
+end;
+
+Function TComponentList.Remove(AComponent: TComponent): Integer;
+begin
+  Result:=Inherited Remove(AComponent);
+end;
+
+Procedure TComponentList.SetItems(Index: Integer; AComponent: TComponent);
+begin
+  Put(Index,AComponent);
+end;
+
+{ TClassList }
+
+Function TClassList.Add(AClass: TClass): Integer;
+begin
+  Result:=Inherited Add(Pointer(AClass));
+end;
+
+Function TClassList.Extract(Item: TClass): TClass;
+begin
+  Result:=TClass(Inherited Extract(Pointer(Item)));
+end;
+
+Function TClassList.First: TClass;
+begin
+  Result:=TClass(Inherited First);
+end;
+
+Function TClassList.GetItems(Index: Integer): TClass;
+begin
+  Result:=TClass(Inherited Items[Index]);
+end;
+
+Function TClassList.IndexOf(AClass: TClass): Integer;
+begin
+  Result:=Inherited IndexOf(Pointer(AClass));
+end;
+
+Procedure TClassList.Insert(Index: Integer; AClass: TClass);
+begin
+  Inherited Insert(index,Pointer(AClass));
+end;
+
+Function TClassList.Last: TClass;
+begin
+  Result:=TClass(Inherited Last);
+end;
+
+Function TClassList.Remove(AClass: TClass): Integer;
+begin
+  Result:=Inherited Remove(Pointer(AClass));
+end;
+
+Procedure TClassList.SetItems(Index: Integer; AClass: TClass);
+begin
+  Put(Index,Pointer(Aclass));
+end;
+
+{ TOrderedList }
+
+Function TOrderedList.AtLeast(ACount: Integer): Boolean;
+begin
+  Result:=(FList.Count>=Acount)
+end;
+
+Function TOrderedList.Count: Integer;
+begin
+  Result:=FList.Count;
+end;
+
+constructor TOrderedList.Create;
+begin
+  FList:=Tlist.Create;
+end;
+
+destructor TOrderedList.Destroy;
+begin
+  FList.Free;
+end;
+
+Function TOrderedList.Peek: Pointer;
+begin
+  If AtLeast(1) then
+    Result:=PeekItem
+  else
+    Result:=Nil;
+end;
+
+Function TOrderedList.PeekItem: Pointer;
+begin
+  With Flist do
+    Result:=Items[Count-1]
+end;
+
+Function TOrderedList.Pop: Pointer;
+begin
+  If Atleast(1) then
+    Result:=PopItem
+  else
+    Result:=Nil;
+end;
+
+Function TOrderedList.PopItem: Pointer;
+begin
+  With FList do
+    If Count>0 then
+      begin
+      Result:=Items[Count-1];
+      Delete(Count-1);
+      end
+    else
+      Result:=Nil;
+end;
+
+Function TOrderedList.Push(AItem: Pointer): Pointer;
+begin
+  PushItem(Aitem);
+  Result:=AItem;
+end;
+
+{ TStack }
+
+Procedure TStack.PushItem(AItem: Pointer);
+begin
+  FList.Add(Aitem);
+end;
+
+{ TObjectStack }
+
+Function TObjectStack.Peek: TObject;
+begin
+  Result:=TObject(Inherited Peek);
+end;
+
+Function TObjectStack.Pop: TObject;
+begin
+  Result:=TObject(Inherited Pop);
+end;
+
+Function TObjectStack.Push(AObject: TObject): TObject;
+begin
+  Result:=TObject(Inherited Push(Pointer(AObject)));
+end;
+
+{ TQueue }
+
+Procedure TQueue.PushItem(AItem: Pointer);
+begin
+  With Flist Do
+    Insert(0,AItem);
+end;
+
+{ TObjectQueue }
+
+Function TObjectQueue.Peek: TObject;
+begin
+  Result:=TObject(Inherited Peek);
+end;
+
+Function TObjectQueue.Pop: TObject;
+begin
+  Result:=TObject(Inherited Pop);
+end;
+
+Function TObjectQueue.Push(AObject: TObject): TObject;
+begin
+  Result:=TObject(Inherited Push(Pointer(Aobject)));
+end;
+
+
+{*****************************************************************************
+                            TFPHashList
+*****************************************************************************}
+
+    function FPHash1(const s:shortstring):LongWord;
+      Var
+        g : LongWord;
+        p,pmax : pchar;
+      begin
+        result:=0;
+        p:=@s[1];
+        pmax:=@s[length(s)+1];
+        while (p<pmax) do
+          begin
+            result:=result shl 4 + LongWord(p^);
+            g:=result and LongWord($F0000000);
+            if g<>0 then
+              result:=result xor (g shr 24) xor g;
+            inc(p);
+          end;
+        If result=0 then
+          result:=$ffffffff;
+      end;
+
+    function FPHash(const s:shortstring):LongWord;
+      Var
+        p,pmax : pchar;
+      begin
+{$ifopt Q+}
+{$define overflowon}
+{$Q-}
+{$endif}
+        result:=0;
+        p:=@s[1];
+        pmax:=@s[length(s)+1];
+        while (p<pmax) do
+          begin
+            result:=LongWord((result shl 5) - result) xor LongWord(P^);
+            inc(p);
+          end;
+{$ifdef overflowon}
+{$Q+}
+{$undef overflowon}
+{$endif}
+      end;
+
+
+procedure TFPHashList.RaiseIndexError(Index : Integer);
+begin
+  Error(SListIndexError, Index);
+end;
+
+
+function TFPHashList.Get(Index: Integer): Pointer;
+begin
+  If (Index < 0) or (Index >= FCount) then
+    RaiseIndexError(Index);
+  Result:=FHashList^[Index].Data;
+end;
+
+
+procedure TFPHashList.Put(Index: Integer; Item: Pointer);
+begin
+  if (Index < 0) or (Index >= FCount) then
+    RaiseIndexError(Index);
+  FHashList^[Index].Data:=Item;;
+end;
+
+
+function TFPHashList.NameOfIndex(Index: Integer): shortstring;
+begin
+  If (Index < 0) or (Index >= FCount) then
+    RaiseIndexError(Index);
+  with FHashList^[Index] do
+    begin
+      if StrIndex>=0 then
+        Result:=PShortString(@FStrs[StrIndex])^
+      else
+        Result:='';
+    end;
+end;
+
+
+function TFPHashList.HashOfIndex(Index: Integer): LongWord;
+begin
+  If (Index < 0) or (Index >= FCount) then
+    RaiseIndexError(Index);
+  Result:=FHashList^[Index].HashValue;
+end;
+
+
+function TFPHashList.Extract(item: Pointer): Pointer;
+var
+  i : Integer;
+begin
+  result := nil;
+  i := IndexOf(item);
+  if i >= 0 then
+   begin
+     Result := item;
+     Delete(i);
+   end;
+end;
+
+
+procedure TFPHashList.SetCapacity(NewCapacity: Integer);
+begin
+  If (NewCapacity < FCount) or (NewCapacity > MaxHashListSize) then
+     Error (SListCapacityError, NewCapacity);
+  if NewCapacity = FCapacity then
+    exit;
+  ReallocMem(FHashList, NewCapacity*SizeOf(THashItem));
+  FCapacity := NewCapacity;
+end;
+
+
+procedure TFPHashList.SetCount(NewCount: Integer);
+begin
+  if (NewCount < 0) or (NewCount > MaxHashListSize)then
+    Error(SListCountError, NewCount);
+  If NewCount > FCount then
+    begin
+      If NewCount > FCapacity then
+        SetCapacity(NewCount);
+      If FCount < NewCount then
+        FillChar(FHashList^[FCount], (NewCount-FCount) div Sizeof(THashItem), 0);
+    end;
+  FCount := Newcount;
+end;
+
+
+procedure TFPHashList.SetStrCapacity(NewCapacity: Integer);
+begin
+  If (NewCapacity < FStrCount) or (NewCapacity > MaxHashStrSize) then
+     Error (SListCapacityError, NewCapacity);
+  if NewCapacity = FStrCapacity then
+    exit;
+  ReallocMem(FStrs, NewCapacity);
+  FStrCapacity := NewCapacity;
+end;
+
+
+procedure TFPHashList.SetHashCapacity(NewCapacity: Integer);
+begin
+  If (NewCapacity < 1) then
+    Error (SListCapacityError, NewCapacity);
+  if FHashCapacity=NewCapacity then
+    exit;
+  FHashCapacity:=NewCapacity;
+  ReallocMem(FHashTable, FHashCapacity*sizeof(Integer));
+  ReHash;
+end;
+
+
+procedure TFPHashList.ReHash;
+var
+  i : Integer;
+begin
+  FillDword(FHashTable^,FHashCapacity,LongWord(-1));
+  For i:=0 To FCount-1 Do
+    AddToHashTable(i);
+end;
+
+
+constructor TFPHashList.Create;
+begin
+  SetHashCapacity(1);
+end;
+
+
+destructor TFPHashList.Destroy;
+begin
+  Clear;
+  if assigned(FHashTable) then
+    FreeMem(FHashTable);
+  inherited Destroy;
+end;
+
+
+function TFPHashList.AddStr(const s:shortstring): Integer;
+var
+  Len : Integer;
+begin
+  len:=length(s)+1;
+  if FStrCount+Len >= FStrCapacity then
+    StrExpand(Len);
+  System.Move(s[0],FStrs[FStrCount],Len);
+  result:=FStrCount;
+  inc(FStrCount,Len);
+end;
+
+
+procedure TFPHashList.AddToHashTable(Index: Integer);
+var
+  HashIndex : Integer;
+begin
+  with FHashList^[Index] do
+    begin
+      if not assigned(Data) then
+        exit;
+      HashIndex:=HashValue mod LongWord(FHashCapacity);
+      NextIndex:=FHashTable^[HashIndex];
+      FHashTable^[HashIndex]:=Index;
+    end;
+end;
+
+
+function TFPHashList.Add(const AName:shortstring;Item: Pointer): Integer;
+begin
+  if FCount = FCapacity then
+    Expand;
+  with FHashList^[FCount] do
+    begin
+      HashValue:=FPHash(AName);
+      Data:=Item;
+      StrIndex:=AddStr(AName);
+    end;
+  AddToHashTable(FCount);
+  Result := FCount;
+  inc(FCount);
+end;
+
+procedure TFPHashList.Clear;
+begin
+  if Assigned(FHashList) then
+    begin
+      FCount:=0;
+      SetCapacity(0);
+      FHashList := nil;
+    end;
+  SetHashCapacity(1);
+  if Assigned(FStrs) then
+    begin
+      FStrCount:=0;
+      SetStrCapacity(0);
+      FStrs := nil;
+    end;
+end;
+
+procedure TFPHashList.Delete(Index: Integer);
+begin
+  If (Index<0) or (Index>=FCount) then
+    Error (SListIndexError, Index);
+  { Remove from HashList }
+  dec(FCount);
+  System.Move (FHashList^[Index+1], FHashList^[Index], (FCount - Index) * Sizeof(THashItem));
+  { All indexes are updated, we need to build the hashtable again }
+  Rehash;
+  { Shrink the list if appropriate }
+  if (FCapacity > 256) and (FCount < FCapacity shr 2) then
+    begin
+      FCapacity := FCapacity shr 1;
+      ReallocMem(FHashList, Sizeof(THashItem) * FCapacity);
+    end;
+end;
+
+function TFPHashList.Remove(Item: Pointer): Integer;
+begin
+  Result := IndexOf(Item);
+  If Result <> -1 then
+    Self.Delete(Result);
+end;
+
+class procedure TFPHashList.Error(const Msg: string; Data: PtrInt);
+begin
+  Raise EListError.CreateFmt(Msg,[Data]) at get_caller_addr(get_frame);
+end;
+
+function TFPHashList.Expand: TFPHashList;
+var
+  IncSize : Longint;
+begin
+  Result := Self;
+  if FCount < FCapacity then
+    exit;
+  IncSize := sizeof(ptrint)*2;
+  if FCapacity > 127 then
+    Inc(IncSize, FCapacity shr 2)
+  else if FCapacity > sizeof(ptrint)*3 then
+    Inc(IncSize, FCapacity shr 1)
+  else if FCapacity >= sizeof(ptrint) then
+    inc(IncSize,sizeof(ptrint));
+  SetCapacity(FCapacity + IncSize);
+  { Maybe expand hash also }
+  if FCount>FHashCapacity*MaxItemsPerHash then
+    SetHashCapacity(FCount div MaxItemsPerHash);
+end;
+
+procedure TFPHashList.StrExpand(MinIncSize:Integer);
+var
+  IncSize : Longint;
+begin
+  if FStrCount+MinIncSize < FStrCapacity then
+    exit;
+  IncSize := 64;
+  if FStrCapacity > 255 then
+    Inc(IncSize, FStrCapacity shr 2);
+  SetStrCapacity(FStrCapacity + IncSize + MinIncSize);
+end;
+
+function TFPHashList.IndexOf(Item: Pointer): Integer;
+var
+  psrc  : PHashItem;
+  Index : integer;
+begin
+  Result:=-1;
+  psrc:=@FHashList^[0];
+  For Index:=0 To FCount-1 Do
+    begin
+      if psrc^.Data=Item then
+        begin
+          Result:=Index;
+          exit;
+        end;
+      inc(psrc);
+    end;
+end;
+
+function TFPHashList.InternalFind(AHash:LongWord;const AName:shortstring;out PrevIndex:Integer):Integer;
+var
+  HashIndex : Integer;
+  Len,
+  LastChar  : Char;
+begin
+  HashIndex:=AHash mod LongWord(FHashCapacity);
+  Result:=FHashTable^[HashIndex];
+  Len:=Char(Length(AName));
+  LastChar:=AName[Byte(Len)];
+  PrevIndex:=-1;
+  while Result<>-1 do
+    begin
+      with FHashList^[Result] do
+        begin
+          if assigned(Data) and
+             (HashValue=AHash) and
+             (Len=FStrs[StrIndex]) and
+             (LastChar=FStrs[StrIndex+Byte(Len)]) and
+             (AName=PShortString(@FStrs[StrIndex])^) then
+            exit;
+          PrevIndex:=Result;
+          Result:=NextIndex;
+        end;
+    end;
+end;
+
+
+function TFPHashList.Find(const AName:shortstring): Pointer;
+var
+  Index,
+  PrevIndex : Integer;
+begin
+  Result:=nil;
+  Index:=InternalFind(FPHash(AName),AName,PrevIndex);
+  if Index=-1 then
+    exit;
+  Result:=FHashList^[Index].Data;
+end;
+
+
+function TFPHashList.FindIndexOf(const AName:shortstring): Integer;
+var
+  PrevIndex : Integer;
+begin
+  Result:=InternalFind(FPHash(AName),AName,PrevIndex);
+end;
+
+
+function TFPHashList.FindWithHash(const AName:shortstring;AHash:LongWord): Pointer;
+var
+  Index,
+  PrevIndex : Integer;
+begin
+  Result:=nil;
+  Index:=InternalFind(AHash,AName,PrevIndex);
+  if Index=-1 then
+    exit;
+  Result:=FHashList^[Index].Data;
+end;
+
+
+function TFPHashList.Rename(const AOldName,ANewName:shortstring): Integer;
+var
+  PrevIndex,
+  Index : Integer;
+  OldHash : LongWord;
+begin
+  Result:=-1;
+  OldHash:=FPHash(AOldName);
+  Index:=InternalFind(OldHash,AOldName,PrevIndex);
+  if Index=-1 then
+    exit;
+  { Remove from current Hash }
+  if PrevIndex<>-1 then
+    FHashList^[PrevIndex].NextIndex:=FHashList^[Index].NextIndex
+  else
+    FHashTable^[OldHash mod LongWord(FHashCapacity)]:=FHashList^[Index].NextIndex;
+  { Set new name and hash }
+  with FHashList^[Index] do
+    begin
+      HashValue:=FPHash(ANewName);
+      StrIndex:=AddStr(ANewName);
+    end;
+  { Insert back in Hash }
+  AddToHashTable(Index);
+  { Return Index }
+  Result:=Index;
+end;
+
+procedure TFPHashList.Pack;
+var
+  NewCount,
+  i : integer;
+  pdest,
+  psrc : PHashItem;
+begin
+  NewCount:=0;
+  psrc:=@FHashList^[0];
+  pdest:=psrc;
+  For I:=0 To FCount-1 Do
+    begin
+      if assigned(psrc^.Data) then
+        begin
+          pdest^:=psrc^;
+          inc(pdest);
+          inc(NewCount);
+        end;
+      inc(psrc);
+    end;
+  FCount:=NewCount;
+  { We need to ReHash to update the IndexNext }
+  ReHash;
+  { Release over-capacity }
+  SetCapacity(FCount);
+  SetStrCapacity(FStrCount);
+end;
+
+
+procedure TFPHashList.ShowStatistics;
+var
+  HashMean,
+  HashStdDev : Double;
+  Index,
+  i,j : Integer;
+begin
+  { Calculate Mean and StdDev }
+  HashMean:=0;
+  HashStdDev:=0;
+  for i:=0 to FHashCapacity-1 do
+    begin
+      j:=0;
+      Index:=FHashTable^[i];
+      while (Index<>-1) do
+        begin
+          inc(j);
+          Index:=FHashList^[Index].NextIndex;
+        end;
+      HashMean:=HashMean+j;
+      HashStdDev:=HashStdDev+Sqr(j);
+    end;
+  HashMean:=HashMean/FHashCapacity;
+  HashStdDev:=(HashStdDev-FHashCapacity*Sqr(HashMean));
+  If FHashCapacity>1 then
+    HashStdDev:=Sqrt(HashStdDev/(FHashCapacity-1))
+  else
+    HashStdDev:=0;
+  { Print info to stdout }
+  Writeln('HashSize   : ',FHashCapacity);
+  Writeln('HashMean   : ',HashMean:1:4);
+  Writeln('HashStdDev : ',HashStdDev:1:4);
+  Writeln('ListSize   : ',FCount,'/',FCapacity);
+  Writeln('StringSize : ',FStrCount,'/',FStrCapacity);
+end;
+
+
+procedure TFPHashList.ForEachCall(proc2call:TListCallback;arg:pointer);
+var
+  i : integer;
+  p : pointer;
+begin
+  For I:=0 To Count-1 Do
+    begin
+      p:=FHashList^[i].Data;
+      if assigned(p) then
+        proc2call(p,arg);
+    end;
+end;
+
+
+procedure TFPHashList.ForEachCall(proc2call:TListStaticCallback;arg:pointer);
+var
+  i : integer;
+  p : pointer;
+begin
+  For I:=0 To Count-1 Do
+    begin
+      p:=FHashList^[i].Data;
+      if assigned(p) then
+        proc2call(p,arg);
+    end;
+end;
+
+
+{*****************************************************************************
+                               TFPHashObject
+*****************************************************************************}
+
+procedure TFPHashObject.InternalChangeOwner(HashObjectList:TFPHashObjectList;const s:shortstring);
+var
+  Index : integer;
+begin
+  FOwner:=HashObjectList;
+  Index:=HashObjectList.Add(s,Self);
+  FStrIndex:=HashObjectList.List.List^[Index].StrIndex;
+  FCachedStr:=PShortString(@FOwner.List.Strs[FStrIndex]);
+end;
+
+
+constructor TFPHashObject.CreateNotOwned;
+begin
+  FStrIndex:=-1;
+end;
+
+
+constructor TFPHashObject.Create(HashObjectList:TFPHashObjectList;const s:shortstring);
+begin
+  InternalChangeOwner(HashObjectList,s);
+end;
+
+
+procedure TFPHashObject.ChangeOwner(HashObjectList:TFPHashObjectList);
+begin
+  InternalChangeOwner(HashObjectList,PShortString(@FOwner.List.Strs[FStrIndex])^);
+end;
+
+
+procedure TFPHashObject.ChangeOwnerAndName(HashObjectList:TFPHashObjectList;const s:shortstring);
+begin
+  InternalChangeOwner(HashObjectList,s);
+end;
+
+
+procedure TFPHashObject.Rename(const ANewName:shortstring);
+var
+  Index : integer;
+begin
+  Index:=FOwner.Rename(PShortString(@FOwner.List.Strs[FStrIndex])^,ANewName);
+  if Index<>-1 then
+    begin
+      FStrIndex:=FOwner.List.List^[Index].StrIndex;
+      FCachedStr:=PShortString(@FOwner.List.Strs[FStrIndex]);
+    end;
+end;
+
+
+function TFPHashObject.GetName:shortstring;
+begin
+  if FOwner<>nil then
+    begin
+      FCachedStr:=PShortString(@FOwner.List.Strs[FStrIndex]);
+      Result:=FCachedStr^;
+    end
+  else
+    Result:='';
+end;
+
+
+function TFPHashObject.GetHash:Longword;
+begin
+  if FOwner<>nil then
+    Result:=FPHash(PShortString(@FOwner.List.Strs[FStrIndex])^)
+  else
+    Result:=$ffffffff;
+end;
+
+
+{*****************************************************************************
+            TFPHashObjectList (Copied from rtl/objpas/classes/lists.inc)
+*****************************************************************************}
+
+constructor TFPHashObjectList.Create(FreeObjects : boolean = True);
+begin
+  inherited Create;
+  FHashList := TFPHashList.Create;
+  FFreeObjects := Freeobjects;
+end;
+
+destructor TFPHashObjectList.Destroy;
+begin
+  if (FHashList <> nil) then
+    begin
+      Clear;
+      FHashList.Destroy;
+    end;
+  inherited Destroy;
+end;
+
+procedure TFPHashObjectList.Clear;
+var
+  i: integer;
+begin
+  if FFreeObjects then
+    for i := 0 to FHashList.Count - 1 do
+      TObject(FHashList[i]).Free;
+  FHashList.Clear;
+end;
+
+function TFPHashObjectList.GetCount: integer;
+begin
+  Result := FHashList.Count;
+end;
+
+procedure TFPHashObjectList.SetCount(const AValue: integer);
+begin
+  if FHashList.Count <> AValue then
+    FHashList.Count := AValue;
+end;
+
+function TFPHashObjectList.GetItem(Index: Integer): TObject;
+begin
+  Result := TObject(FHashList[Index]);
+end;
+
+procedure TFPHashObjectList.SetItem(Index: Integer; AObject: TObject);
+begin
+  if OwnsObjects then
+    TObject(FHashList[Index]).Free;
+  FHashList[index] := AObject;
+end;
+
+procedure TFPHashObjectList.SetCapacity(NewCapacity: Integer);
+begin
+  FHashList.Capacity := NewCapacity;
+end;
+
+function TFPHashObjectList.GetCapacity: integer;
+begin
+  Result := FHashList.Capacity;
+end;
+
+function TFPHashObjectList.Add(const AName:shortstring;AObject: TObject): Integer;
+begin
+  Result := FHashList.Add(AName,AObject);
+end;
+
+function TFPHashObjectList.NameOfIndex(Index: Integer): shortstring;
+begin
+  Result := FHashList.NameOfIndex(Index);
+end;
+
+function TFPHashObjectList.HashOfIndex(Index: Integer): LongWord;
+begin
+  Result := FHashList.HashOfIndex(Index);
+end;
+
+procedure TFPHashObjectList.Delete(Index: Integer);
+begin
+  if OwnsObjects then
+    TObject(FHashList[Index]).Free;
+  FHashList.Delete(Index);
+end;
+
+function TFPHashObjectList.Expand: TFPHashObjectList;
+begin
+  FHashList.Expand;
+  Result := Self;
+end;
+
+function TFPHashObjectList.Extract(Item: TObject): TObject;
+begin
+  Result := TObject(FHashList.Extract(Item));
+end;
+
+function TFPHashObjectList.Remove(AObject: TObject): Integer;
+begin
+  Result := IndexOf(AObject);
+  if (Result <> -1) then
+    begin
+      if OwnsObjects then
+        TObject(FHashList[Result]).Free;
+      FHashList.Delete(Result);
+    end;
+end;
+
+function TFPHashObjectList.IndexOf(AObject: TObject): Integer;
+begin
+  Result := FHashList.IndexOf(Pointer(AObject));
+end;
+
+
+function TFPHashObjectList.Find(const s:shortstring): TObject;
+begin
+  result:=TObject(FHashList.Find(s));
+end;
+
+
+function TFPHashObjectList.FindIndexOf(const s:shortstring): Integer;
+begin
+  result:=FHashList.FindIndexOf(s);
+end;
+
+
+function TFPHashObjectList.FindWithHash(const AName:shortstring;AHash:LongWord): Pointer;
+begin
+  Result:=TObject(FHashList.FindWithHash(AName,AHash));
+end;
+
+
+function TFPHashObjectList.Rename(const AOldName,ANewName:shortstring): Integer;
+begin
+  Result:=FHashList.Rename(AOldName,ANewName);
+end;
+
+
+function TFPHashObjectList.FindInstanceOf(AClass: TClass; AExact: Boolean; AStartAt : Integer): Integer;
+var
+  I : Integer;
+begin
+  I:=AStartAt;
+  Result:=-1;
+  If AExact then
+    while (I<Count) and (Result=-1) do
+      If Items[i].ClassType=AClass then
+        Result:=I
+      else
+        Inc(I)
+  else
+    while (I<Count) and (Result=-1) do
+      If Items[i].InheritsFrom(AClass) then
+        Result:=I
+      else
+        Inc(I);
+end;
+
+
+procedure TFPHashObjectList.Pack;
+begin
+  FHashList.Pack;
+end;
+
+
+procedure TFPHashObjectList.ShowStatistics;
+begin
+  FHashList.ShowStatistics;
+end;
+
+
+procedure TFPHashObjectList.ForEachCall(proc2call:TObjectListCallback;arg:pointer);
+begin
+  FHashList.ForEachCall(TListCallBack(proc2call),arg);
+end;
+
+
+procedure TFPHashObjectList.ForEachCall(proc2call:TObjectListStaticCallback;arg:pointer);
+begin
+  FHashList.ForEachCall(TListStaticCallBack(proc2call),arg);
+end;
+
+
+{ ---------------------------------------------------------------------
+    Hash support, by Dean Zobec
+  ---------------------------------------------------------------------}
+
+{ Default hash function }
+
+function RSHash(const S: string; const TableSize: Longword): Longword;
+const
+  b = 378551;
+var
+  a: Longword;
+  i: Longword;
+begin
+ a := 63689;
+ Result := 0;
+ if length(s)>0 then
+   for i := 1 to Length(S) do
+   begin
+     Result := Result * a + Ord(S[i]);
+     a := a * b;
+   end;
+ Result := (Result and $7FFFFFFF) mod TableSize;
+end;
+
+{ THTNode }
+
+constructor THTCustomNode.CreateWith(const AString: string);
+begin
+  inherited Create;
+  FKey := AString;
+end;
+
+function THTCustomNode.HasKey(const AKey: string): boolean;
+begin
+  if Length(AKey) <> Length(FKey) then
+  begin
+    Result := false;
+    exit;
+  end
+  else
+    Result := CompareMem(PChar(FKey), PChar(AKey), length(AKey));
+end;
+
+{ TFPCustomHashTable }
+
+constructor TFPCustomHashTable.Create;
+begin
+  CreateWith(196613,@RSHash);
+end;
+
+constructor TFPCustomHashTable.CreateWith(AHashTableSize: Longword;
+  aHashFunc: THashFunction);
+begin
+  Inherited Create;
+  FHashTable := TFPObjectList.Create(True);
+  HashTableSize := AHashTableSize;
+  FHashFunction := aHashFunc;
+end;
+
+destructor TFPCustomHashTable.Destroy;
+begin
+  FHashTable.Free;
+  inherited Destroy;
+end;
+
+function TFPCustomHashTable.GetDensity: Longword;
+begin
+  Result := FHashTableSize - VoidSlots
+end;
+
+function TFPCustomHashTable.GetNumberOfCollisions: Longword;
+begin
+  Result := FCount -(FHashTableSize - VoidSlots)
+end;
+
+procedure TFPCustomHashTable.SetHashTableSize(const Value: Longword);
+var
+  i: Longword;
+  newSize: Longword;
+begin
+  if Value <> FHashTableSize then
+  begin
+    i := 0;
+    while (PRIMELIST[i] < Value) and (i < 27) do
+     inc(i);
+    newSize := PRIMELIST[i];
+    if Count = 0 then
+    begin
+      FHashTableSize := newSize;
+      InitializeHashTable;
+    end
+    else
+      ChangeTableSize(newSize);
+  end;
+end;
+
+procedure TFPCustomHashTable.InitializeHashTable;
+var
+  i: LongWord;
+begin
+  if FHashTableSize>0 Then
+    for i := 0 to FHashTableSize-1 do
+     FHashTable.Add(nil);
+  FCount := 0;
+end;
+
+procedure TFPCustomHashTable.ChangeTableSize(const ANewSize: Longword);
+var
+  SavedTable: TFPObjectList;
+  SavedTableSize: Longword;
+  i, j: Longword;
+  temp: THTCustomNode;
+begin
+  SavedTable := FHashTable;
+  SavedTableSize := FHashTableSize;
+  FHashTableSize := ANewSize;
+  FHashTable := TFPObjectList.Create(True);
+  InitializeHashTable;
+  If SavedTableSize>0 Then
+    for i := 0 to SavedTableSize-1 do
+    begin
+      if Assigned(SavedTable[i]) then
+      for j := 0 to TFPObjectList(SavedTable[i]).Count -1 do
+      begin
+        temp := THTCustomNode(TFPObjectList(SavedTable[i])[j]);
+        AddNode(temp);
+      end;
+    end;
+  SavedTable.Free;
+end;
+
+procedure TFPCustomHashTable.SetHashFunction(AHashFunction: THashFunction);
+begin
+  if IsEmpty then
+    FHashFunction := AHashFunction
+  else
+    raise Exception.Create(NotEmptyMsg);
+end;
+
+function TFPCustomHashTable.Find(const aKey: string): THTCustomNode;
+var
+  hashCode: Longword;
+  chn: TFPObjectList;
+  i: Longword;
+begin
+  hashCode := FHashFunction(aKey, FHashTableSize);
+  chn := Chain(hashCode);
+  if Assigned(chn) then
+  begin
+    if chn.count>0 then
+     for i := 0 to chn.Count - 1 do
+      if THTCustomNode(chn[i]).HasKey(aKey) then
+      begin
+        result := THTCustomNode(chn[i]);
+        exit;
+      end;
+  end;
+  Result := nil;
+end;
+
+Function TFPCustomHashTable.FindChainForAdd(Const aKey : String) : TFPObjectList;
+
+var
+  hashCode: Longword;
+  i: Longword;
+
+begin
+  hashCode := FHashFunction(aKey, FHashTableSize);
+  Result := Chain(hashCode);
+  if Assigned(Result)  then
+    begin
+    if Result.count>0 then
+      for i := 0 to Result.Count - 1 do
+        if THTCustomNode(Result[i]).HasKey(aKey) then
+          Raise EDuplicate.CreateFmt(DuplicateMsg, [aKey]);
+    end
+  else
+    begin
+    FHashTable[hashcode] := TFPObjectList.Create(true);
+    Result := Chain(hashcode);
+    end;
+  inc(FCount);
+end;
+
+
+procedure TFPCustomHashTable.Delete(const aKey: string);
+var
+  hashCode: Longword;
+  chn: TFPObjectList;
+  i: Longword;
+begin
+  hashCode := FHashFunction(aKey, FHashTableSize);
+  chn := Chain(hashCode);
+  if Assigned(chn) then
+  begin
+    if chn.count>0 then
+    for i := 0 to chn.Count - 1 do
+      if THTCustomNode(chn[i]).HasKey(aKey) then
+      begin
+        chn.Delete(i);
+        dec(FCount);
+        exit;
+      end;
+  end;
+  raise EKeyNotFound.CreateFmt(KeyNotFoundMsg, ['Delete', aKey]);
+end;
+
+function TFPCustomHashTable.IsEmpty: boolean;
+begin
+  Result := (FCount = 0);
+end;
+
+function TFPCustomHashTable.Chain(const index: Longword): TFPObjectList;
+begin
+  Result := TFPObjectList(FHashTable[index]);
+end;
+
+function TFPCustomHashTable.GetVoidSlots: Longword;
+var
+  i: Longword;
+  num: Longword;
+begin
+  num := 0;
+  if FHashTableSize>0 Then
+    for i:= 0 to FHashTableSize-1 do
+      if Not Assigned(Chain(i)) then
+        inc(num);
+  result := num;
+end;
+
+function TFPCustomHashTable.GetLoadFactor: double;
+begin
+  Result := Count / FHashTableSize;
+end;
+
+function TFPCustomHashTable.GetAVGChainLen: double;
+begin
+  result := Count / (FHashTableSize - VoidSlots);
+end;
+
+function TFPCustomHashTable.GetMaxChainLength: Longword;
+var
+  i: Longword;
+begin
+  Result := 0;
+  if FHashTableSize>0 Then
+   for i := 0 to FHashTableSize-1 do
+      if ChainLength(i) > Result then
+        Result := ChainLength(i);
+end;
+
+function TFPCustomHashTable.FindOrCreateNew(const aKey: string): THTCustomNode;
+var
+  hashCode: Longword;
+  chn: TFPObjectList;
+  i: Longword;
+begin
+  hashCode := FHashFunction(aKey, FHashTableSize);
+  chn := Chain(hashCode);
+  if Assigned(chn)  then
+  begin
+    if chn.count>0 then
+     for i := 0 to chn.Count - 1 do
+      if THTCustomNode(chn[i]).HasKey(aKey) then
+        begin
+          Result := THTNode(chn[i]);
+          exit;
+        end
+  end
+  else
+    begin
+      FHashTable[hashcode] := TFPObjectList.Create(true);
+      chn := Chain(hashcode);
+    end;
+  inc(FCount);
+  Result := CreateNewNode(aKey);
+  chn.Add(Result);
+end;
+
+function TFPCustomHashTable.ChainLength(const ChainIndex: Longword): Longword;
+begin
+  if Assigned(Chain(ChainIndex)) then
+    Result := Chain(ChainIndex).Count
+  else
+    Result := 0;
+end;
+
+procedure TFPCustomHashTable.Clear;
+var
+  i: Longword;
+begin
+  if FHashTableSize>0 Then
+    for i := 0 to FHashTableSize - 1 do
+      begin
+        if Assigned(Chain(i)) then
+          Chain(i).Clear;
+      end;
+  FCount := 0;
+end;
+
+
+
+{ TFPDataHashTable }
+
+procedure TFPDataHashTable.Add(const aKey: string; aItem: pointer);
+var
+  chn: TFPObjectList;
+  NewNode: THtDataNode;
+begin
+  chn:=FindChainForAdd(akey);
+  NewNode := THtDataNode(CreateNewNode(aKey));
+  NewNode.Data := aItem;
+  chn.Add(NewNode);
+end;
+
+function TFPDataHashTable.GetData(const Index: string): Pointer;
+var
+  node: THTDataNode;
+begin
+  node := THTDataNode(Find(Index));
+  if Assigned(node) then
+    Result := node.Data
+  else
+    Result := nil;
+end;
+
+procedure TFPDataHashTable.SetData(const index: string; const AValue: Pointer);
+begin
+  THTDataNode(FindOrCreateNew(index)).Data := AValue;
+end;
+
+Function TFPDataHashTable.CreateNewNode(const aKey : string) : THTCustomNode;
+
+begin
+  Result:=THTDataNode.CreateWith(aKey);
+end;
+
+function TFPDataHashTable.ForEachCall(aMethod: TDataIteratorMethod): THTDataNode;
+var
+  i, j: Longword;
+  continue: boolean;
+begin
+  Result := nil;
+  continue := true;
+  if FHashTableSize>0 then
+   for i := 0 to FHashTableSize-1 do
+    begin
+      if assigned(Chain(i)) then
+      begin
+       if chain(i).count>0 then
+        for j := 0 to Chain(i).Count-1 do
+        begin
+          aMethod(THTDataNode(Chain(i)[j]).Data, THTDataNode(Chain(i)[j]).Key, continue);
+          if not continue then
+          begin
+            Result := THTDataNode(Chain(i)[j]);
+            Exit;
+          end;
+        end;
+      end;
+    end;
+end;
+
+Procedure TFPDataHashTable.AddNode(ANode : THTCustomNode);
+
+begin
+  With THTDataNode(ANode) do
+    Add(Key,Data);
+end;
+
+{ TFPStringHashTable }
+
+Procedure TFPStringHashTable.AddNode(ANode : THTCustomNode);
+
+begin
+  With THTStringNode(ANode) do
+    Add(Key,Data);
+end;
+
+function TFPStringHashTable.GetData(const Index: string): String;
+var
+  node: THTStringNode;
+begin
+  node := THTStringNode(Find(Index));
+  if Assigned(node) then
+    Result := node.Data
+  else
+    Result := '';
+end;
+
+procedure TFPStringHashTable.SetData(const index, AValue: string);
+begin
+  THTStringNode(FindOrCreateNew(index)).Data := AValue;
+end;
+
+procedure TFPStringHashTable.Add(const aKey, aItem: string);
+var
+  chn: TFPObjectList;
+  NewNode: THtStringNode;
+
+begin
+  chn:=FindChainForAdd(akey);
+  NewNode := THtStringNode(CreateNewNode(aKey));
+  NewNode.Data := aItem;
+  chn.Add(NewNode);
+end;
+
+Function TFPStringHashTable.CreateNewNode(const aKey : string) : THTCustomNode;
+
+begin
+  Result:=THTStringNode.CreateWith(aKey);
+end;
+
+
+function TFPStringHashTable.ForEachCall(aMethod: TStringIteratorMethod): THTStringNode;
+var
+  i, j: Longword;
+  continue: boolean;
+begin
+  Result := nil;
+  continue := true;
+  if FHashTableSize>0 then
+   for i := 0 to FHashTableSize-1 do
+    begin
+      if assigned(Chain(i)) then
+      begin
+       if chain(i).count>0 then
+        for j := 0 to Chain(i).Count-1 do
+        begin
+          aMethod(THTStringNode(Chain(i)[j]).Data, THTStringNode(Chain(i)[j]).Key, continue);
+          if not continue then
+          begin
+            Result := THTStringNode(Chain(i)[j]);
+            Exit;
+          end;
+        end;
+      end;
+    end;
+end;
+
+{ TFPObjectHashTable }
+
+Procedure TFPObjectHashTable.AddNode(ANode : THTCustomNode);
+
+begin
+  With THTObjectNode(ANode) do
+    Add(Key,Data);
+end;
+
+function TFPObjectHashTable.GetData(const Index: string): TObject;
+var
+  node: THTObjectNode;
+begin
+  node := THTObjectNode(Find(Index));
+  if Assigned(node) then
+    Result := node.Data
+  else
+    Result := Nil;
+end;
+
+procedure TFPObjectHashTable.SetData(const index : string; AObject : TObject);
+begin
+  THTObjectNode(FindOrCreateNew(index)).Data := AObject;
+end;
+
+procedure TFPObjectHashTable.Add(const aKey: string; AItem : TObject);
+var
+  chn: TFPObjectList;
+  NewNode: THTObjectNode;
+
+begin
+  chn:=FindChainForAdd(akey);
+  NewNode := THTObjectNode(CreateNewNode(aKey));
+  NewNode.Data := aItem;
+  chn.Add(NewNode);
+end;
+
+Function TFPObjectHashTable.CreateNewNode(const aKey : string) : THTCustomNode;
+
+begin
+  If OwnsObjects then
+    Result:=THTOwnedObjectNode.CreateWith(aKey)
+  else
+    Result:=THTObjectNode.CreateWith(aKey);
+end;
+
+
+function TFPObjectHashTable.ForEachCall(aMethod: TObjectIteratorMethod): THTObjectNode;
+var
+  i, j: Longword;
+  continue: boolean;
+begin
+  Result := nil;
+  continue := true;
+  if FHashTableSize>0 then
+   for i := 0 to FHashTableSize-1 do
+    begin
+      if assigned(Chain(i)) then
+      begin
+       if chain(i).count>0 then
+        for j := 0 to Chain(i).Count-1 do
+        begin
+          aMethod(THTObjectNode(Chain(i)[j]).Data, THTObjectNode(Chain(i)[j]).Key, continue);
+          if not continue then
+          begin
+            Result := THTObjectNode(Chain(i)[j]);
+            Exit;
+          end;
+        end;
+      end;
+    end;
+end;
+
+constructor TFPObjectHashTable.Create(AOwnsObjects : Boolean = True);
+
+begin
+  Inherited Create;
+  FOwnsObjects:=AOwnsObjects;
+end;
+
+constructor TFPObjectHashTable.CreateWith(AHashTableSize: Longword; aHashFunc: THashFunction; AOwnsObjects : Boolean = True);
+
+begin
+  Inherited CreateWith(AHashTableSize,AHashFunc);
+  FOwnsObjects:=AOwnsObjects;
+end;
+
+Destructor THTOwnedObjectNode.Destroy;
+
+begin
+  FreeAndNil(FData);
+  Inherited;
+end;
+
+end.

+ 185 - 90
utils/fppkg/fppkg.lpi

@@ -1,7 +1,7 @@
 <?xml version="1.0"?>
 <CONFIG>
   <ProjectOptions>
-    <PathDelim Value="/"/>
+    <PathDelim Value="\"/>
     <Version Value="5"/>
     <General>
       <Flags>
@@ -10,7 +10,7 @@
         <MainUnitHasTitleStatement Value="False"/>
       </Flags>
       <MainUnit Value="0"/>
-      <IconPath Value="./"/>
+      <IconPath Value=".\"/>
       <TargetFileExt Value=""/>
       <ActiveEditorIndexAtStart Value="0"/>
     </General>
@@ -21,6 +21,7 @@
     </VersionInfo>
     <PublishOptions>
       <Version Value="2"/>
+      <DestinationDirectory Value="$(TestDir)\publishedproject\"/>
       <IgnoreBinaries Value="False"/>
       <IncludeFileFilter Value="*.(pas|pp|inc|lfm|lpr|lrs|lpi|lpk|sh|xml)"/>
       <ExcludeFileFilter Value="*.(bak|ppu|ppw|o|so);*~;backup"/>
@@ -28,35 +29,36 @@
     <RunParams>
       <local>
         <FormatVersion Value="1"/>
-        <LaunchingApplication PathPlusParams="/usr/X11R6/bin/xterm -T 'Lazarus Run Output' -e $(LazarusDir)/tools/runwait.sh $(TargetCmdLine)"/>
+        <LaunchingApplication PathPlusParams="\usr\X11R6\bin\xterm -T 'Lazarus Run Output' -e $(LazarusDir)\tools\runwait.sh $(TargetCmdLine)"/>
       </local>
     </RunParams>
-    <Units Count="11">
+    <Units Count="20">
       <Unit0>
         <Filename Value="fppkg.pp"/>
         <IsPartOfProject Value="True"/>
         <UnitName Value="fppkg"/>
-        <CursorPos X="1" Y="244"/>
-        <TopLine Value="215"/>
+        <CursorPos X="32" Y="231"/>
+        <TopLine Value="212"/>
         <EditorIndex Value="0"/>
-        <UsageCount Value="21"/>
+        <UsageCount Value="33"/>
         <Loaded Value="True"/>
       </Unit0>
       <Unit1>
         <Filename Value="pkgropts.pp"/>
         <IsPartOfProject Value="True"/>
         <UnitName Value="pkgropts"/>
-        <CursorPos X="3" Y="25"/>
-        <TopLine Value="1"/>
-        <EditorIndex Value="1"/>
-        <UsageCount Value="21"/>
+        <CursorPos X="19" Y="25"/>
+        <TopLine Value="22"/>
+        <EditorIndex Value="6"/>
+        <UsageCount Value="33"/>
         <Loaded Value="True"/>
       </Unit1>
       <Unit2>
         <Filename Value="fpmkcnst.inc"/>
         <IsPartOfProject Value="True"/>
-        <UsageCount Value="21"/>
-        <SyntaxHighlighter Value="Text"/>
+        <CursorPos X="64" Y="8"/>
+        <TopLine Value="1"/>
+        <UsageCount Value="33"/>
       </Unit2>
       <Unit3>
         <Filename Value="fpmktype.pp"/>
@@ -64,197 +66,290 @@
         <UnitName Value="fpmktype"/>
         <CursorPos X="3" Y="41"/>
         <TopLine Value="1"/>
-        <EditorIndex Value="3"/>
-        <UsageCount Value="21"/>
+        <EditorIndex Value="9"/>
+        <UsageCount Value="33"/>
         <Loaded Value="True"/>
       </Unit3>
       <Unit4>
         <Filename Value="fpmkunit.pp"/>
         <IsPartOfProject Value="True"/>
-        <UsageCount Value="21"/>
-        <SyntaxHighlighter Value="Text"/>
+        <UnitName Value="fpmkunit"/>
+        <CursorPos X="1" Y="1"/>
+        <TopLine Value="1"/>
+        <EditorIndex Value="1"/>
+        <UsageCount Value="33"/>
+        <Loaded Value="True"/>
       </Unit4>
       <Unit5>
         <Filename Value="fprepos.pp"/>
         <IsPartOfProject Value="True"/>
-        <UsageCount Value="21"/>
-        <SyntaxHighlighter Value="Text"/>
+        <UnitName Value="fprepos"/>
+        <CursorPos X="27" Y="28"/>
+        <TopLine Value="2"/>
+        <EditorIndex Value="5"/>
+        <UsageCount Value="33"/>
+        <Loaded Value="True"/>
       </Unit5>
       <Unit6>
         <Filename Value="fpxmlrep.pp"/>
         <IsPartOfProject Value="True"/>
-        <UsageCount Value="21"/>
+        <UsageCount Value="33"/>
         <SyntaxHighlighter Value="Text"/>
       </Unit6>
       <Unit7>
         <Filename Value="pkghandler.pp"/>
         <IsPartOfProject Value="True"/>
         <UnitName Value="pkghandler"/>
-        <CursorPos X="19" Y="60"/>
-        <TopLine Value="17"/>
-        <EditorIndex Value="6"/>
-        <UsageCount Value="21"/>
+        <CursorPos X="52" Y="51"/>
+        <TopLine Value="32"/>
+        <EditorIndex Value="13"/>
+        <UsageCount Value="33"/>
         <Loaded Value="True"/>
       </Unit7>
       <Unit8>
         <Filename Value="pkgmkconv.pp"/>
         <IsPartOfProject Value="True"/>
         <UnitName Value="pkgmkconv"/>
-        <CursorPos X="20" Y="7"/>
-        <TopLine Value="1"/>
-        <EditorIndex Value="5"/>
-        <UsageCount Value="21"/>
+        <CursorPos X="1" Y="46"/>
+        <TopLine Value="20"/>
+        <EditorIndex Value="11"/>
+        <UsageCount Value="33"/>
         <Loaded Value="True"/>
       </Unit8>
       <Unit9>
         <Filename Value="pkgdownload.pp"/>
         <IsPartOfProject Value="True"/>
         <UnitName Value="pkgdownload"/>
-        <CursorPos X="13" Y="96"/>
-        <TopLine Value="56"/>
-        <EditorIndex Value="4"/>
-        <UsageCount Value="21"/>
+        <CursorPos X="32" Y="18"/>
+        <TopLine Value="5"/>
+        <EditorIndex Value="10"/>
+        <UsageCount Value="33"/>
         <Loaded Value="True"/>
       </Unit9>
       <Unit10>
         <Filename Value="pkgmessages.pp"/>
         <IsPartOfProject Value="True"/>
         <UnitName Value="pkgmessages"/>
-        <CursorPos X="26" Y="9"/>
+        <CursorPos X="69" Y="12"/>
         <TopLine Value="1"/>
-        <EditorIndex Value="2"/>
-        <UsageCount Value="21"/>
+        <EditorIndex Value="8"/>
+        <UsageCount Value="33"/>
         <Loaded Value="True"/>
       </Unit10>
+      <Unit11>
+        <Filename Value="streamcoll.pp"/>
+        <UnitName Value="streamcoll"/>
+        <CursorPos X="66" Y="88"/>
+        <TopLine Value="65"/>
+        <UsageCount Value="9"/>
+      </Unit11>
+      <Unit12>
+        <Filename Value="streamcoll20.pp"/>
+        <UnitName Value="streamcoll"/>
+        <CursorPos X="3" Y="15"/>
+        <TopLine Value="1"/>
+        <UsageCount Value="9"/>
+      </Unit12>
+      <Unit13>
+        <Filename Value="..\..\..\fpc20\rtl\objpas\classes\classesh.inc"/>
+        <CursorPos X="17" Y="1345"/>
+        <TopLine Value="1326"/>
+        <EditorIndex Value="2"/>
+        <UsageCount Value="16"/>
+        <Loaded Value="True"/>
+      </Unit13>
+      <Unit14>
+        <Filename Value="..\..\..\fpc20\rtl\inc\objpash.inc"/>
+        <CursorPos X="38" Y="277"/>
+        <TopLine Value="269"/>
+        <EditorIndex Value="12"/>
+        <UsageCount Value="16"/>
+        <Loaded Value="True"/>
+      </Unit14>
+      <Unit15>
+        <Filename Value="..\..\..\fpc20\fcl\inc\contnrs.pp"/>
+        <UnitName Value="contnrs"/>
+        <CursorPos X="1" Y="19"/>
+        <TopLine Value="1"/>
+        <UsageCount Value="9"/>
+      </Unit15>
+      <Unit16>
+        <Filename Value="contnrs20.pp"/>
+        <UnitName Value="contnrs"/>
+        <CursorPos X="43" Y="32"/>
+        <TopLine Value="1"/>
+        <EditorIndex Value="3"/>
+        <UsageCount Value="10"/>
+        <Loaded Value="True"/>
+      </Unit16>
+      <Unit17>
+        <Filename Value="pkgfpmake.pp"/>
+        <UnitName Value="pkgfpmake"/>
+        <CursorPos X="45" Y="85"/>
+        <TopLine Value="1"/>
+        <EditorIndex Value="7"/>
+        <UsageCount Value="15"/>
+        <Loaded Value="True"/>
+      </Unit17>
+      <Unit18>
+        <Filename Value="pkgwget.pp"/>
+        <UnitName Value="pkgwget"/>
+        <CursorPos X="23" Y="6"/>
+        <TopLine Value="1"/>
+        <EditorIndex Value="4"/>
+        <UsageCount Value="10"/>
+        <Loaded Value="True"/>
+      </Unit18>
+      <Unit19>
+        <Filename Value="pkglnet.pas"/>
+        <UnitName Value="pkglnet"/>
+        <CursorPos X="5" Y="140"/>
+        <TopLine Value="103"/>
+        <UsageCount Value="10"/>
+      </Unit19>
     </Units>
     <JumpHistory Count="30" HistoryIndex="29">
       <Position1>
         <Filename Value="fppkg.pp"/>
-        <Caret Line="245" Column="5" TopLine="222"/>
+        <Caret Line="218" Column="42" TopLine="206"/>
       </Position1>
       <Position2>
-        <Filename Value="fppkg.pp"/>
-        <Caret Line="278" Column="1" TopLine="245"/>
+        <Filename Value="pkgfpmake.pp"/>
+        <Caret Line="70" Column="18" TopLine="56"/>
       </Position2>
       <Position3>
-        <Filename Value="fppkg.pp"/>
-        <Caret Line="245" Column="5" TopLine="222"/>
+        <Filename Value="pkgfpmake.pp"/>
+        <Caret Line="10" Column="5" TopLine="1"/>
       </Position3>
       <Position4>
-        <Filename Value="fppkg.pp"/>
-        <Caret Line="249" Column="12" TopLine="222"/>
+        <Filename Value="pkgmkconv.pp"/>
+        <Caret Line="679" Column="46" TopLine="666"/>
       </Position4>
       <Position5>
-        <Filename Value="pkghandler.pp"/>
-        <Caret Line="58" Column="65" TopLine="35"/>
+        <Filename Value="pkgfpmake.pp"/>
+        <Caret Line="67" Column="1" TopLine="39"/>
       </Position5>
       <Position6>
-        <Filename Value="pkghandler.pp"/>
-        <Caret Line="47" Column="51" TopLine="24"/>
+        <Filename Value="pkgfpmake.pp"/>
+        <Caret Line="28" Column="1" TopLine="8"/>
       </Position6>
       <Position7>
-        <Filename Value="pkghandler.pp"/>
-        <Caret Line="54" Column="1" TopLine="31"/>
+        <Filename Value="pkgfpmake.pp"/>
+        <Caret Line="43" Column="26" TopLine="24"/>
       </Position7>
       <Position8>
-        <Filename Value="pkghandler.pp"/>
-        <Caret Line="47" Column="1" TopLine="47"/>
+        <Filename Value="pkgmessages.pp"/>
+        <Caret Line="11" Column="20" TopLine="1"/>
       </Position8>
       <Position9>
-        <Filename Value="pkghandler.pp"/>
-        <Caret Line="54" Column="3" TopLine="31"/>
+        <Filename Value="pkgfpmake.pp"/>
+        <Caret Line="51" Column="11" TopLine="32"/>
       </Position9>
       <Position10>
-        <Filename Value="pkghandler.pp"/>
-        <Caret Line="59" Column="3" TopLine="36"/>
+        <Filename Value="pkgfpmake.pp"/>
+        <Caret Line="35" Column="3" TopLine="20"/>
       </Position10>
       <Position11>
-        <Filename Value="pkghandler.pp"/>
-        <Caret Line="53" Column="6" TopLine="30"/>
+        <Filename Value="fppkg.pp"/>
+        <Caret Line="99" Column="19" TopLine="76"/>
       </Position11>
       <Position12>
-        <Filename Value="pkghandler.pp"/>
-        <Caret Line="40" Column="47" TopLine="30"/>
+        <Filename Value="pkgfpmake.pp"/>
+        <Caret Line="56" Column="6" TopLine="37"/>
       </Position12>
       <Position13>
-        <Filename Value="pkghandler.pp"/>
-        <Caret Line="53" Column="6" TopLine="30"/>
+        <Filename Value="fppkg.pp"/>
+        <Caret Line="86" Column="26" TopLine="76"/>
       </Position13>
       <Position14>
         <Filename Value="pkghandler.pp"/>
-        <Caret Line="55" Column="10" TopLine="30"/>
+        <Caret Line="47" Column="1" TopLine="22"/>
       </Position14>
       <Position15>
-        <Filename Value="pkghandler.pp"/>
-        <Caret Line="50" Column="14" TopLine="9"/>
+        <Filename Value="pkgfpmake.pp"/>
+        <Caret Line="56" Column="23" TopLine="37"/>
       </Position15>
       <Position16>
-        <Filename Value="pkghandler.pp"/>
-        <Caret Line="53" Column="6" TopLine="30"/>
+        <Filename Value="pkgfpmake.pp"/>
+        <Caret Line="58" Column="16" TopLine="39"/>
       </Position16>
       <Position17>
-        <Filename Value="pkghandler.pp"/>
-        <Caret Line="40" Column="10" TopLine="17"/>
+        <Filename Value="pkgfpmake.pp"/>
+        <Caret Line="74" Column="1" TopLine="55"/>
       </Position17>
       <Position18>
-        <Filename Value="fppkg.pp"/>
-        <Caret Line="46" Column="1" TopLine="23"/>
+        <Filename Value="pkgfpmake.pp"/>
+        <Caret Line="25" Column="1" TopLine="1"/>
       </Position18>
       <Position19>
-        <Filename Value="fppkg.pp"/>
-        <Caret Line="289" Column="1" TopLine="266"/>
+        <Filename Value="pkgfpmake.pp"/>
+        <Caret Line="105" Column="23" TopLine="75"/>
       </Position19>
       <Position20>
-        <Filename Value="fppkg.pp"/>
-        <Caret Line="254" Column="1" TopLine="241"/>
+        <Filename Value="pkgfpmake.pp"/>
+        <Caret Line="89" Column="17" TopLine="70"/>
       </Position20>
       <Position21>
-        <Filename Value="fppkg.pp"/>
-        <Caret Line="46" Column="42" TopLine="23"/>
+        <Filename Value="pkgfpmake.pp"/>
+        <Caret Line="100" Column="34" TopLine="71"/>
       </Position21>
       <Position22>
-        <Filename Value="fppkg.pp"/>
-        <Caret Line="33" Column="28" TopLine="23"/>
+        <Filename Value="pkgfpmake.pp"/>
+        <Caret Line="86" Column="13" TopLine="70"/>
       </Position22>
       <Position23>
         <Filename Value="fppkg.pp"/>
-        <Caret Line="47" Column="42" TopLine="23"/>
+        <Caret Line="29" Column="1" TopLine="20"/>
       </Position23>
       <Position24>
-        <Filename Value="fppkg.pp"/>
-        <Caret Line="253" Column="42" TopLine="230"/>
+        <Filename Value="pkgfpmake.pp"/>
+        <Caret Line="90" Column="23" TopLine="71"/>
       </Position24>
       <Position25>
-        <Filename Value="fppkg.pp"/>
-        <Caret Line="254" Column="37" TopLine="231"/>
+        <Filename Value="pkgfpmake.pp"/>
+        <Caret Line="88" Column="1" TopLine="72"/>
       </Position25>
       <Position26>
         <Filename Value="fppkg.pp"/>
-        <Caret Line="259" Column="9" TopLine="236"/>
+        <Caret Line="42" Column="25" TopLine="17"/>
       </Position26>
       <Position27>
-        <Filename Value="fppkg.pp"/>
-        <Caret Line="234" Column="16" TopLine="203"/>
+        <Filename Value="pkghandler.pp"/>
+        <Caret Line="51" Column="19" TopLine="42"/>
       </Position27>
       <Position28>
-        <Filename Value="fppkg.pp"/>
-        <Caret Line="278" Column="63" TopLine="255"/>
+        <Filename Value="pkghandler.pp"/>
+        <Caret Line="214" Column="24" TopLine="181"/>
       </Position28>
       <Position29>
         <Filename Value="fppkg.pp"/>
-        <Caret Line="283" Column="1" TopLine="255"/>
+        <Caret Line="233" Column="19" TopLine="199"/>
       </Position29>
       <Position30>
         <Filename Value="fppkg.pp"/>
-        <Caret Line="282" Column="9" TopLine="259"/>
+        <Caret Line="231" Column="39" TopLine="214"/>
       </Position30>
     </JumpHistory>
   </ProjectOptions>
   <CompilerOptions>
     <Version Value="5"/>
+    <PathDelim Value="\"/>
     <CodeGeneration>
-      <Generate Value="Faster"/>
+      <Checks>
+        <IOChecks Value="True"/>
+        <RangeChecks Value="True"/>
+        <OverflowChecks Value="True"/>
+        <StackChecks Value="True"/>
+      </Checks>
+      <Optimizations>
+        <OptimizationLevel Value="0"/>
+      </Optimizations>
     </CodeGeneration>
+    <Linking>
+      <Debugging>
+        <GenerateDebugInfo Value="True"/>
+      </Debugging>
+    </Linking>
     <Other>
       <CompilerPath Value="$(CompPath)"/>
     </Other>

+ 129 - 195
utils/fppkg/fppkg.pp

@@ -11,41 +11,23 @@ uses
   // Repository handler objects
   fprepos, fpxmlrep,fpmktype, pkgropts,
   // Package Handler components
-  pkghandler, pkgmkconv, pkgdownload, pkgmessages;
-  
-Type
-
-  TRunMode = (rmHelp,rmCompile,rmBuild,rmInstall,rmArchive,rmClean,rmDownload,rmUpdate);
+  pkghandler, pkgmkconv, pkgdownload, pkgfpmake, pkgmessages;
 
+Type
   { TMakeTool }
 
   TMakeTool = Class(TCustomApplication)
   Private
     FDefaults: TPackagerOptions;
-    FConvertOnly,
-    FLogging : Boolean;
     FCompiler : String;
-    FRunMode : TRunMode;
-    FHaveMakefile : Boolean;
-    FHaveFpmake : Boolean;
-    FFPMakeSrc : String;
-    FFPMakeBin : String;
-    FVerbose: TVerbosities;
-    FPackages : TStrings;
-    Procedure Log(Msg : String);
-    Procedure Error(Msg : String);
-    Procedure Error(Fmt : String; Args : Array of const);
     Function GetCompiler : String;
+    procedure ShowUsage;
   Public
-    Procedure DownloadFile(Const URL,Dest : String);
     Function GetConfigFileName : String;
     Procedure LoadDefaults;
     Procedure ProcessCommandLine;
-    procedure CreateFPMake;
-    procedure CompileFPMake(Extra : Boolean);
-    Function RunFPMake : Integer;
     Procedure DoRun; Override;
-    Property Verbose : TVerbosities Read FVerbose Write FVerbose;
+    procedure ExecuteAction(const AAction:string;const Args:TActionArgs);
   end;
 
   EMakeToolError = Class(Exception);
@@ -53,78 +35,6 @@ Type
 
 { TMakeTool }
 
-procedure TMakeTool.CompileFPMake(Extra: Boolean);
-
-Var
-  O,C : String;
-
-begin
-  C:=GetCompiler;
-  O:=FFPmakeSrc;
-  If Extra then
-    O:='-Fafpmkext '+O;
-  Log(SLogCompilingFPMake+C+' '+O);
-  If ExecuteProcess(C,O)<>0 then
-    Error(SErrFailedToCompileFPCMake)
-end;
-
-procedure TMakeTool.CreateFPMake;
-begin
-  Log(SLogGeneratingFPMake);
-  With TMakeFileConverter.Create(Nil) do
-    try
-      ConvertFile('Makefile.fpc','fpmake.pp');
-    finally
-      Free;
-    end;
-end;
-
-
-Function TMakeTool.RunFPMake : Integer;
-
-  Function MaybeQuote(Const S : String) : String;
-  
-  begin
-    If Pos(' ',S)=0 then
-      Result:=S
-    else
-      Result:='"'+S+'"';
-  end;
-  
-
-Var
-  I : integer;
-  D,O : String;
-
-begin
-  Log(SLogRunningFPMake);
-  D:=IncludeTrailingPathDelimiter(GetCurrentDir);
-  O:='';
-  For I:=1 to ParamCount do
-    begin
-    If (O<>'') then
-      O:=O+' ';
-    O:=O+MaybeQuote(ParamStr(I));
-    end;
-  Result:=ExecuteProcess(D+FFPMakeBin,O);
-end;
-
-procedure TMakeTool.Log(Msg: String);
-begin
-  If FLogging then
-    Writeln(stdErr,Msg);
-end;
-
-procedure TMakeTool.Error(Msg: String);
-begin
-  Raise EMakeToolError.Create(Msg);
-end;
-
-procedure TMakeTool.Error(Fmt: String; Args: array of const);
-begin
-  Raise EMakeToolError.CreateFmt(Fmt,Args);
-end;
-
 function TMakeTool.GetCompiler: String;
 begin
   If (FCompiler='') then
@@ -151,22 +61,16 @@ begin
     Result:=FCompiler
   else
     begin
-    Result:=FileSearch(FCompiler,GetEnvironmentVariable('PATH'));
+    Result:=FileSearch(FCompiler+ExeExt,GetEnvironmentVariable('PATH'));
     If (Result='') then
-      Result:=FCompiler;
+      Result:=FCompiler+ExeExt;
     end;
 end;
 
-procedure TMakeTool.DownloadFile(const URL, Dest: String);
-begin
-
-end;
 
 function TMakeTool.GetConfigFileName: String;
-
 var
   G : Boolean;
-
 begin
   if HasOption('C','config-file') then
     Result:=GetOptionValue('C','config-file')
@@ -181,13 +85,32 @@ begin
     end
 end;
 
+
 procedure TMakeTool.LoadDefaults;
 begin
+  Verbosity:=[vError,vInfo,vCommands,vDebug];
   FDefaults:=TPackagerOptions.Create;
   FDefaults.LoadFromFile(GetConfigFileName);
 end;
 
 
+procedure TMakeTool.ShowUsage;
+begin
+  Writeln('Usage: ',Paramstr(0),' [options] <action> <package>');
+  Writeln('Options:');
+  Writeln('  -r --compiler      Set compiler');
+  Writeln('  -h --help          This help');
+  Writeln('  -v --verbose       Set verbosity');
+  Writeln('Actions:');
+  Writeln('  update             Update available packages');
+  Writeln('  listpackages       List available packages');
+  Writeln('  build              Build package');
+  Writeln('  install            Install package');
+  Writeln('  download           Download package');
+  Writeln('  convertmk          Convert Makefile.fpc to fpmake.pp');
+end;
+
+
 procedure TMakeTool.ProcessCommandLine;
 
   Function CheckOption(Index : Integer;Short,Long : String): Boolean;
@@ -208,122 +131,133 @@ procedure TMakeTool.ProcessCommandLine;
   begin
     if (Length(ParamStr(Index))>1) and (Paramstr(Index)[2]<>'-') then
       begin
-      If Index<ParamCount then
-        begin
-        Inc(Index);
-        Result:=Paramstr(Index);
-        end
-      else
-        Error(SErrNeedArgument,[Index,ParamStr(Index)]);
+        If Index<ParamCount then
+          begin
+            Inc(Index);
+            Result:=Paramstr(Index);
+          end
+        else
+          Error(SErrNeedArgument,[Index,ParamStr(Index)]);
       end
     else If length(ParamStr(Index))>2 then
       begin
-      P:=Pos('=',Paramstr(Index));
-      If (P=0) then
-        Error(SErrNeedArgument,[Index,ParamStr(Index)])
-      else
-        begin
-        Result:=Paramstr(Index);
-        Delete(Result,1,P);
-        end;
+        P:=Pos('=',Paramstr(Index));
+        If (P=0) then
+          Error(SErrNeedArgument,[Index,ParamStr(Index)])
+        else
+          begin
+            Result:=Paramstr(Index);
+            Delete(Result,1,P);
+          end;
       end;
   end;
 
 Var
   I : Integer;
-  GlobalOpts : Boolean;
-  cmd : string;
-  
+  Action : string;
+  ParaPackages : TStringList;
+  HasAction : Boolean;
 begin
-  I:=0;
-  FLogging:=False;
-  FRunMode:=rmhelp;
-  FConvertOnly:=False;
-  GlobalOpts:=True;
-  FPackages:=TStringList.Create;
-  // We can't use the TCustomApplication option handling,
-  // because they cannot handle [general opts] [command] [cmd-opts] [args]
-  While (I<ParamCount) do
-    begin
-    Inc(I);
-    // Check options.
-    if CheckOption(I,'r','compiler') then
-      FDefaults.Compiler:=OptionArg(I)
-    else if CheckOption(I,'v','verbose') then
-      Include(FVerbose,StringToVerbosity(OptionArg(I)))
-    else if CheckOption(I,'h','help') then
-      FRunMode:=rmhelp
-    else if (Length(Paramstr(i))>0) and (Paramstr(I)[1]='-') then
-      Raise EMakeToolError.CreateFmt(SErrInvalidArgument,[I,ParamStr(i)])
-    else
-      If GlobalOpts then
-        begin
-        // It's a command.
-        Cmd:=Paramstr(I);
-        if (Cmd='convert') then
-          FConvertOnly:=True
-        else if (Cmd='compile') then
-          FRunMode:=rmCompile
-        else if (Cmd='build') then
-          FRunMode:=rmBuild
-        else if (Cmd='install') then
-          FRunMode:=rmInstall
-        else if (cmd='clean') then
-          FRunMode:=rmClean
-        else if (cmd='archive') then
-          FRunMode:=rmarchive
-        else if (cmd='download') then
-          FRunMode:=rmDownload
-        else if (cmd='update') then
-          FRunMode:=rmUpdate
+  try
+    I:=0;
+    HasAction:=false;
+    ParaPackages:=TStringList.Create;
+    // We can't use the TCustomApplication option handling,
+    // because they cannot handle [general opts] [command] [cmd-opts] [args]
+    While (I<ParamCount) do
+      begin
+        Inc(I);
+        // Check options.
+        if CheckOption(I,'r','compiler') then
+          FDefaults.Compiler:=OptionArg(I)
+        else if CheckOption(I,'v','verbose') then
+          Include(Verbosity,StringToVerbosity(OptionArg(I)))
+        else if CheckOption(I,'h','help') then
+          begin
+            ShowUsage;
+            halt(0);
+          end
+        else if (Length(Paramstr(i))>0) and (Paramstr(I)[1]='-') then
+          Raise EMakeToolError.CreateFmt(SErrInvalidArgument,[I,ParamStr(i)])
+        else
+        // It's a command or target.
+          begin
+            if HasAction then
+              ParaPackages.Add(Paramstr(i))
+            else
+              begin
+                Action:=Paramstr(i);
+                HasAction:=true;
+              end;
+          end;
+      end;
+    if HasAction then
+      begin
+        if GetPkgHandler(Action)<>nil then
+          begin
+            for i:=0 to ParaPackages.Count-1 do
+              ActionStack.Push(Action,[ParaPackages[i]])
+          end
         else
-          Raise EMakeToolError.CreateFmt(SErrInvalidCommand,[Cmd]);
-        end
-      else // It's a package name.
+          Raise EMakeToolError.CreateFmt(SErrInvalidCommand,[Action]);
+      end
+    else
+      ShowUsage;
+  finally
+    FreeAndNil(ParaPackages);
+  end;
+end;
+
+
+procedure TMakeTool.ExecuteAction(const AAction:string;const Args:TActionArgs);
+var
+  pkghandlerclass : TPackageHandlerClass;
+  i : integer;
+  logargs : string;
+begin
+  if vDebug in Verbosity then
+    begin
+      logargs:='';
+      for i:=Low(Args) to High(Args) do
         begin
-        FPackages.Add(Paramstr(i));
+          if logargs='' then
+            logargs:=Args[i]
+          else
+            logargs:=logargs+','+Args[i];
         end;
+      Log(vDebug,SLogRunAction,[AAction,logargs]);
+    end;
+  pkghandlerclass:=GetPkgHandler(AAction);
+  With pkghandlerclass.Create(FDefaults) do
+    try
+      Execute(Args);
+    finally
+      Free;
     end;
 end;
 
-procedure TMakeTool.DoRun;
-
 
+procedure TMakeTool.DoRun;
+var
+  Action : string;
+  Args   : TActionArgs;
 begin
   LoadDefaults;
   Try
     ProcessCommandLine;
-    If FConvertOnly then
-      CreateFPMake
-    else
-      begin
-      FHaveMakefile:=FileExists('Makefile.fpc');
-      FFPMakeSrc:='fpmake.pp';
-      FHaveFpmake:=FileExists(FFPMakeSrc);
-      If Not FHaveFPMake then
-        begin
-        FHaveFPMake:=FileExists('fpmake.pas');
-        If FHaveFPMake then
-          FFPMakeSrc:='fpmake.pas';
-        end;
-      if Not (FHaveFPMake or FHaveMakeFile) then
-        Error(SErrMissingConfig);
-      If (Not FHaveFPMake) or (FileAge(FFPMakeSrc)<FileAge('Makefile.fpc')) then
-        CreateFPMake;
-    {$ifndef unix}
-      FFPMakeBin:='fpmake.exe';
-    {$else}
-      FFPMakeBin:='fpmake';
-    {$endif}
-      if FileAge(FFPMakeBin)<FileAge(FFPMakeSrc) then
-        CompileFPMake(FRunMode in [rmArchive,rmDownload]);
-      Halt(RunFPMake);
-      end;
+    
+    repeat
+      if not ActionStack.Pop(Action,Args) then
+        break;
+      ExecuteAction(Action,Args);
+    until false;
+    Terminate;
+    
   except
     On E : Exception do
       begin
-      Writeln(StdErr,Format(SErrRunning,[E.Message]));
-      Halt(1);
+        Writeln(StdErr,Format(SErrRunning,[E.Message]));
+        Halt(1);
       end;
   end;
 end;

+ 9 - 1
utils/fppkg/fprepos.pp

@@ -16,7 +16,15 @@ unit fprepos;
 
 interface
 
-uses classes,sysutils,streamcoll,contnrs,fpmktype;
+uses
+  classes,sysutils,
+  contnrs,
+{$ifdef ver2_0}
+  streamcoll20,
+{$else}
+  streamcoll,
+{$endif}
+  fpmktype;
 
 Const 
   StreamVersion   : Integer = 1;

+ 1 - 1
utils/fppkg/lnet/lftp.pp

@@ -234,7 +234,7 @@ const
                                                 'RenameTo', 'System', 'Features',
                                                 'PWD', 'HELP', 'LAST');
 
-procedure Writedbg(const ar: array of const);
+procedure Writedbg(const ar: array of string);
 {$ifdef debug}
 var
   i: Integer;

+ 110 - 0
utils/fppkg/pkgfpmake.pp

@@ -0,0 +1,110 @@
+unit pkgfpmake;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+  Classes, SysUtils,pkghandler;
+
+type
+  { TFPMakeCompiler }
+
+  TFPMakeCompiler = Class(TPackagehandler)
+  Private
+    Procedure CompileFPMake;
+  Public
+    Function Execute(const Args:array of string):boolean;override;
+  end;
+
+
+  { TFPMakeRunner }
+
+  TFPMakeRunner = Class(TPackagehandler)
+  Private
+    Function RunFPMake : Integer;
+  Public
+    Function Execute(const Args:array of string):boolean;override;
+  end;
+
+
+implementation
+
+uses
+  pkgmessages;
+  
+{ TFPMakeCompiler }
+
+Procedure TFPMakeCompiler.CompileFPMake;
+Var
+  O,C : String;
+  FPMakeSrc : string;
+  HaveFpmake : boolean;
+begin
+  { Check for fpmake source }
+  FPMakeSrc:='fpmake.pp';
+  HaveFpmake:=FileExists(FPMakeSrc);
+  If Not HaveFPMake then
+    begin
+      HaveFPMake:=FileExists('fpmake.pas');
+      If HaveFPMake then
+        FPMakeSrc:='fpmake.pas';
+    end;
+  if Not HaveFPMake then
+    Error(SErrMissingFPMake);
+  { Call compiler }
+  C:=Defaults.Compiler;
+  O:=FPmakeSrc;
+  Log(vCommands,SLogCompilingFPMake+C+' '+O);
+  If ExecuteProcess(C,O)<>0 then
+    Error(SErrFailedToCompileFPCMake)
+end;
+
+
+function TFPMakeCompiler.Execute(const Args:array of string):boolean;
+begin
+{$warning TODO Check arguments}
+  CompileFPMake;
+  result:=true;
+end;
+
+
+{ TFPMakeRunner }
+
+Function TFPMakeRunner.RunFPMake : Integer;
+
+  Function MaybeQuote(Const S : String) : String;
+  begin
+    If Pos(' ',S)=0 then
+      Result:=S
+    else
+      Result:='"'+S+'"';
+  end;
+
+Var
+  I : integer;
+  FPMakeBin,
+  D,O : String;
+begin
+  FPMakeBin:='fpmake'+ExeExt;
+  D:=IncludeTrailingPathDelimiter(GetCurrentDir);
+  O:='';
+  For I:=1 to ParamCount do
+    begin
+    If (O<>'') then
+      O:=O+' ';
+    O:=O+MaybeQuote(ParamStr(I));
+    end;
+  Log(vCommands,SLogRunningFPMake+D+FPMakeBin+' '+O);
+  Result:=ExecuteProcess(D+FPMakeBin,O);
+end;
+
+
+function TFPMakeRunner.Execute(const Args:array of string):boolean;
+begin
+{$warning TODO Check arguments}
+  result:=(RunFPMake=0);
+end;
+
+
+end.

+ 207 - 44
utils/fppkg/pkghandler.pp

@@ -4,51 +4,115 @@ unit pkghandler;
 
 interface
 
-uses Classes,SysUtils, fpmktype;
+uses Classes,SysUtils, fpmktype, pkgropts;
 
-Type
+Const
+{$ifdef unix}
+  ExeExt = '';
+{$else unix}
+  ExeExt = '.exe';
+{$endif unix}
 
+Type
   TVerbosity = (vError,vInfo,vCommands,vDebug);
   TVerbosities = Set of TVerbosity;
-  TMessageEvent = Procedure (Sender : TObject; Const Msg : String) of object;
-  
+
+  { TActionStack }
+
+  TActionArgs = array of string;
+
+  TActionStackItem = record
+    Action : string;
+    Args   : TActionArgs;
+  end;
+  PActionStackItem = ^TActionStackItem;
+
+  TActionStack = class
+  private
+    FList : TFPList;
+  public
+    constructor Create;
+    destructor Destroy;
+    procedure Push(const AAction:string;const Args:TActionArgs);
+    procedure Push(const AAction:string;const Args:array of string);
+    function  Pop(out AAction:string;out Args:TActionArgs):boolean;
+  end;
+
+
   { TPackageHandler }
 
   TPackageHandler = Class(TComponent)
   private
-    FBackupFile: Boolean;
-    FOnMessage: TMessageEvent;
-    FVerbosity: TVerbosities;
+    FBackupFile : Boolean;
+    FDefaults   : TPackagerOptions;
   Protected
-    Procedure Error(Const Msg : String);
-    Procedure Error(Const Fmt : String; Args : Array of const);
-  Public
     Procedure BackupFile(Const FileName : String);
-    Constructor Create(AOwner : TComponent); override;
-    Procedure Verbose(Msg : String); 
-    Procedure Verbose(Fmt : String; Args : Array of const); 
-    Procedure Verbose(Level : TVerbosity; Msg : String);
-    Procedure Verbose(Level : TVerbosity; Fmt : String; Args : Array of const);
+  Public
+    Constructor Create(ADefaults:TPackagerOptions);
+    Function Execute(const Args:array of string):boolean; virtual; abstract;
     Property BackupFiles : Boolean Read FBackupFile Write FBackupFile;
-    Property OnMessage : TMessageEvent Read FOnMessage Write FOnMessage;
-    Property Verbosity : TVerbosities Read FVerbosity Write FVerbosity;
+    Property Defaults:TPackagerOptions Read FDefaults;
   end;
-  
+  TPackageHandlerClass = class(TPackageHandler);
+
   EPackageHandler = Class(EInstallerError);
 
+// Actions/PkgHandler
+procedure RegisterPkgHandler(const AAction:string;pkghandlerclass:TPackageHandlerClass);
+function GetPkgHandler(const AAction:string):TPackageHandlerClass;
+
+// Logging
 Function StringToVerbosity (S : String) : TVerbosity;
 Function VerbosityToString (V : TVerbosity): String;
+Procedure Log(Level: TVerbosity;Msg : String);
+Procedure Log(Level: TVerbosity;Fmt : String; const Args : array of const);
+Procedure Error(Msg : String);
+Procedure Error(Fmt : String; const Args : array of const);
+
+// Utils
+function maybequoted(const s:ansistring):ansistring;
 
+var
+  Verbosity : TVerbosities;
+  ActionStack : TActionStack;
   
+
 Implementation
 
-uses pkgmessages,typinfo;
+uses
+  typinfo,
+{$ifdef ver2_0}
+  contnrs20,
+{$else ver2_0}
+  contnrs,
+{$endif ver2_0}
+  pkgmessages;
+
+var
+  PkgHandlerList : TFPHashObjectList;
+
+procedure RegisterPkgHandler(const AAction:string;pkghandlerclass:TPackageHandlerClass);
+begin
+  if PkgHandlerList.Find(AAction)<>nil then
+    begin
+      Raise EPackageHandler.CreateFmt(SErrActionAlreadyRegistered,[AAction]);
+      exit;
+    end;
+  PkgHandlerList.Add(AAction,pkghandlerclass);
+end;
 
-function StringToVerbosity(S: String): TVerbosity;
 
+function GetPkgHandler(const AAction:string):TPackageHandlerClass;
+begin
+  result:=TPackageHandlerClass(PkgHandlerList.Find(AAction));
+  if result=nil then
+    Raise EPackageHandler.CreateFmt(SErrActionNotFound,[AAction]);
+end;
+
+
+function StringToVerbosity(S: String): TVerbosity;
 Var
   I : integer;
-
 begin
   I:=GetEnumValue(TypeInfo(TVerbosity),'v'+S);
   If (I<>-1) then
@@ -63,58 +127,157 @@ begin
   Delete(Result,1,1);// Delete 'v'
 end;
 
-{ TPackageHandler }
 
-procedure TPackageHandler.Error(const Msg: String);
+procedure Log(Level:TVerbosity;Msg: String);
 begin
-  Raise EPackageHandler.CreateFmt('%s : %s',[ClassName,Msg]);
+  if Level in Verbosity then
+    Writeln(stdErr,Msg);
 end;
 
-procedure TPackageHandler.Error(const Fmt: String; Args: Array of const);
+
+Procedure Log(Level:TVerbosity; Fmt:String; const Args:array of const);
 begin
-  Error(Format(Fmt,Args));
+  Log(Level,Format(Fmt,Args));
 end;
 
-procedure TPackageHandler.BackupFile(const FileName: String);
 
+procedure Error(Msg: String);
+begin
+  Raise EPackageHandler.Create(Msg);
+end;
+
+
+procedure Error(Fmt: String; const Args: array of const);
+begin
+  Raise EPackageHandler.CreateFmt(Fmt,Args);
+end;
+
+
+function maybequoted(const s:ansistring):ansistring;
+const
+  {$IFDEF MSWINDOWS}
+    FORBIDDEN_CHARS = ['!', '@', '#', '$', '%', '^', '&', '*', '(', ')',
+                       '{', '}', '''', '`', '~'];
+  {$ELSE}
+    FORBIDDEN_CHARS = ['!', '@', '#', '$', '%', '^', '&', '*', '(', ')',
+                       '{', '}', '''', ':', '\', '`', '~'];
+  {$ENDIF}
+var
+  s1 : ansistring;
+  i  : integer;
+  quoted : boolean;
+begin
+  quoted:=false;
+  s1:='"';
+  for i:=1 to length(s) do
+   begin
+     case s[i] of
+       '"' :
+         begin
+           quoted:=true;
+           s1:=s1+'\"';
+         end;
+       ' ',
+       #128..#255 :
+         begin
+           quoted:=true;
+           s1:=s1+s[i];
+         end;
+       else begin
+         if s[i] in FORBIDDEN_CHARS then
+           quoted:=True;
+         s1:=s1+s[i];
+       end;
+     end;
+   end;
+  if quoted then
+    maybequoted:=s1+'"'
+  else
+    maybequoted:=s;
+end;
+
+
+{ TPackageHandler }
+
+procedure TPackageHandler.BackupFile(const FileName: String);
 Var
   BFN : String;
-  
-
 begin
   BFN:=FileName+'.bak';
   If not RenameFile(FileName,BFN) then
     Error(SErrBackupFailed,[FileName,BFN]);
 end;
 
-constructor TPackageHandler.Create(AOwner: TComponent);
+constructor TPackageHandler.Create(ADefaults:TPackagerOptions);
 begin
-  inherited Create(AOwner);
-  FVerbosity:=[vError];
+  inherited Create(nil);
+  FDefaults:=ADefaults;
 end;
 
-procedure TPackageHandler.Verbose(Msg: String);
+
+{ TActionStack }
+
+constructor TActionStack.Create;
 begin
-  Verbose(vInfo,Msg);
+  FList:=TFPList.Create;
 end;
 
-procedure TPackageHandler.Verbose(Fmt: String; Args: array of const);
+
+destructor TActionStack.Destroy;
 begin
-  Verbose(vInfo,Fmt,Args);
+  FreeAndNil(FList);
 end;
 
-procedure TPackageHandler.Verbose(Level: TVerbosity; Msg: String);
+
+procedure TActionStack.Push(const AAction:string;const Args:TActionArgs);
+var
+  ActionItem : PActionStackItem;
 begin
-  If (Level in FVerbosity) and Assigned(FOnMessage) then
-    FOnMessage(Self,Msg);
+  New(ActionItem);
+  ActionItem^.Action:=AAction;
+  ActionItem^.Args:=Args;
+  FList.Add(ActionItem);
 end;
 
-procedure TPackageHandler.Verbose(Level: TVerbosity; Fmt: String;
-  Args: array of const);
+
+procedure TActionStack.Push(const AAction:string;const Args:array of string);
+var
+  ActionArgs : TActionArgs;
+  i : integer;
 begin
-  // Save a format call
-  If (Level in FVerbosity) and Assigned(FOnMessage) then
-    Verbose(Level,Format(Fmt,Args));
+  SetLength(ActionArgs,high(Args)+1);
+  for i:=low(Args) to high(Args) do
+    ActionArgs[i]:=Args[i];
+  Push(AAction,ActionArgs);
 end;
 
+
+function TActionStack.Pop(out AAction:string;out Args:TActionArgs):boolean;
+var
+  ActionItem : PActionStackItem;
+  Idx : integer;
+begin
+  Result:=false;
+  if FList.Count=0 then
+    exit;
+  // Retrieve Item from stack
+  Idx:=FList.Count-1;
+  ActionItem:=PActionStackItem(FList[Idx]);
+  FList.Delete(Idx);
+  // Copy contents and dispose stack item
+  AAction:=ActionItem^.Action;
+  Args:=ActionItem^.Args;
+  dispose(ActionItem);
+  Result:=true;
+end;
+
+
+
+
+initialization
+  PkgHandlerList:=TFPHashObjectList.Create(true);
+  ActionStack:=TActionStack.Create;
+finalization
+  FreeAndNil(PkgHandlerList);
+  FreeAndNil(ActionStack);
 end.

+ 1 - 1
utils/fppkg/pkglnet.pas

@@ -39,7 +39,7 @@ Type
 implementation
 
 uses
-  pkgmessages, uriparser;
+  pkghandler,pkgmessages, uriparser;
 
 { TLNetDownloader }
 

+ 7 - 2
utils/fppkg/pkgmessages.pp

@@ -8,8 +8,11 @@ interface
 Resourcestring
   SErrInValidArgument        = 'Invalid command-line argument at position %d : %s';
   SErrNeedArgument           = 'Option at position %d (%s) needs an argument';
-  SErrMissingConfig          = 'Missing configuration Makefile.fpc or fpmake.pp';
+  SErrMissingFPMake          = 'Missing configuration fpmake.pp';
+  SErrMissingMakefilefpc     = 'Missing configuration Makefile.fpc';
   SErrRunning                = 'The FPC make tool encountered the following error: %s';
+  SErrActionAlreadyRegistered= 'Action "%s" is already registered';
+  SErrActionNotFound         = 'Action "%s" is not supported';
   SErrFailedToCompileFPCMake = 'Could not compile fpmake driver program';
   SErrNoFTPDownload          = 'This binary has no support for FTP downloads.';
   SErrNoHTTPDownload         = 'This binary has no support for HTTP downloads.';
@@ -25,10 +28,12 @@ Resourcestring
   SErrLoginFailed            = 'FTP LOGIN command failed.';
   SErrCWDFailed              = 'FTP CWD "%s" command failed.';  
   SErrGETFailed              = 'FTP GET "%s" command failed.';
+  
   SLogGeneratingFPMake       = 'Generating fpmake.pp';
   SLogCompilingFPMake        = 'Compiling fpmake.pp: ';
   SLogRunningFPMake          = 'Running fpmake';
-  
+  SLogRunAction              = 'Action: %s %s';
+
 implementation
 
 end.

+ 13 - 4
utils/fppkg/pkgmkconv.pp

@@ -7,7 +7,7 @@ interface
 uses
   Classes, SysUtils,pkghandler;
   { TMakeFileConverter }
-  
+
 Type
   TSectionType = (stNone,stPackage,stTarget,stclean,stinstall,stCompiler,
                   stDefault,stRequire,stRules,stPrerules);
@@ -39,14 +39,16 @@ Type
     Procedure StartInstaller(Src : TStrings);
     Procedure EndInstaller(Src : TStrings);
     Function GetLine (L : TStrings; Var I : Integer) : String;
-  Public
     procedure ConvertFile(const AFileName: String; Src: TStrings; Dir,OS : String);
     Procedure ConvertFile(Const Source,Dest: String);
+  Public
+    Function Execute(const Args:array of string):boolean;override;
   end;
 
+
 implementation
 
-uses typinfo;
+uses typinfo,pkgmessages;
 
 Function GetWord(var S : String; Sep : Char) : String;
 
@@ -680,6 +682,7 @@ Var
   L : TStrings;
 
 begin
+  Log(vInfo,SLogGeneratingFPMake);
   L:=TStringList.Create;
   Try
     StartInstaller(L);
@@ -691,5 +694,11 @@ begin
   end;
 end;
 
-end.
+function TMakeFileConverter.Execute(const Args:array of string):boolean;
+begin
+{$warning TODO Check arguments}
+  ConvertFile(Args[1],Args[2]);
+  result:=true;
+end;
 
+end.

+ 2 - 2
utils/fppkg/pkgwget.pp

@@ -21,7 +21,7 @@ Type
 
 implementation
 
-uses process,pkgmessages;
+uses process,pkghandler,pkgmessages;
 
 Constructor TWGetDownloader.Create(AOWner : TComponent); 
 
@@ -70,4 +70,4 @@ end;
 
 initialization
   DownloaderClass:=TWGetDownloader;
-end.
+end.

+ 363 - 0
utils/fppkg/streamcoll20.pp

@@ -0,0 +1,363 @@
+{
+    This file is part of the Free Component Library (FCL)
+    Copyright (c) 1999-2000 by the Free Pascal development team
+
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+
+{$ifndef ver2_0}
+  {$fatal This unit is only for compiling with 2.0.x, use the streamcoll from the FCL}
+{$endif}
+
+{$mode objfpc}
+{$h+}
+unit streamcoll20;
+
+interface
+
+uses
+  Classes,SysUtils;
+
+type
+  TStreamCollectionItem = Class(TCollectionItem)
+  Protected
+    Procedure WriteInteger(S : TStream; AValue : Integer);
+    Procedure WriteBoolean(S : TStream; AValue : Boolean);
+    Procedure WriteString(S : TStream; AValue : String);
+    Procedure WriteCurrency(S : TStream; AValue : Currency);
+    Procedure WriteDateTime(S : TStream; AValue : TDateTime);
+    Procedure WriteFloat(S : TStream; AValue : Double);
+    Function ReadInteger(S : TStream) : Integer;
+    Function ReadBoolean(S : TStream) : Boolean;
+    Function ReadString(S : TStream) : String;
+    Function ReadCurrency(S : TStream) : Currency;
+    Function ReadDateTime(S : TStream) : TDateTime;
+    Function ReadFloat(S : TStream) : Double;
+    Procedure LoadFromStream(S : TStream; Streamversion : Integer); virtual; abstract;
+    Procedure SaveToStream(S : TStream); virtual; abstract;
+  end;
+
+  TStreamCollection = Class(TCollection)
+  Private
+    FStreaming : Boolean;
+  Protected
+    Procedure WriteInteger(S : TStream; AValue : Integer);
+    Procedure WriteBoolean(S : TStream; AValue : Boolean);
+    Procedure WriteString(S : TStream; AValue : String);
+    Procedure WriteCurrency(S : TStream; AValue : Currency);
+    Procedure WriteDateTime(S : TStream; AValue : TDateTime);
+    Procedure WriteFloat(S : TStream; AValue : Double);
+    Function ReadInteger(S : TStream) : Integer;
+    Function ReadBoolean(S : TStream) : Boolean;
+    Function ReadString(S : TStream) : String;
+    Function ReadCurrency(S : TStream) : Currency;
+    Function ReadDateTime(S : TStream) : TDateTime;
+    Function ReadFloat(S : TStream) : Double;
+    Procedure DoSaveToStream(S : TStream); virtual;
+    Function CurrentStreamVersion : Integer; Virtual;
+    Procedure DoLoadFromStream(S : TStream; Streamversion : Integer); virtual;
+  Public
+    Procedure LoadFromStream(S : TStream);
+    Procedure SaveToStream(S : TStream);
+    Property Streaming : Boolean Read FStreaming;
+  end;
+
+
+  EStreamColl = Class(Exception);
+
+Procedure ColWriteInteger(S : TStream; AValue : Integer);
+Procedure ColWriteBoolean(S : TStream; AValue : Boolean);
+Procedure ColWriteString(S : TStream; AValue : String);
+Procedure ColWriteCurrency(S : TStream; AValue : Currency);
+Procedure ColWriteDateTime(S : TStream; AValue : TDateTime);
+Procedure ColWriteFloat(S : TStream; AValue : Double);
+Function ColReadInteger(S : TStream) : Integer;
+Function ColReadBoolean(S : TStream) : Boolean;
+Function ColReadString(S : TStream) : String;
+Function ColReadCurrency(S : TStream) : Currency;
+Function ColReadDateTime(S : TStream) : TDateTime;
+Function ColReadFloat(S : TStream) : Double;
+
+implementation
+
+Resourcestring
+  SErrIllegalStreamVersion = 'Illegal stream version: %d > %d.';
+
+Procedure ColWriteInteger(S : TStream; AValue : Integer);
+
+begin
+  S.WriteBuffer(AValue,SizeOf(Integer));
+end;
+
+Procedure ColWriteBoolean(S : TStream; AValue : Boolean);
+
+begin
+  ColWriteInteger(S,Ord(AValue));
+end;
+
+Procedure ColWriteString(S : TStream; AValue : String);
+
+Var
+  L : Integer;
+
+begin
+  L:=Length(AValue);
+  ColWriteInteger(S,L);
+  If (L>0) then
+    S.WriteBuffer(AValue[1],L);
+end;
+
+Procedure ColWriteCurrency(S : TStream; AValue : Currency);
+
+begin
+  S.WriteBuffer(AValue,SizeOf(Currency));
+end;
+
+Procedure ColWriteDateTime(S : TStream; AValue : TDateTime);
+
+begin
+  S.WriteBuffer(AValue,SizeOf(TDateTime));
+end;
+
+Procedure ColWriteFloat(S : TStream; AValue : Double);
+
+begin
+  S.WriteBuffer(AValue,SizeOf(Double));
+end;
+
+Function ColReadInteger(S : TStream) : Integer;
+
+begin
+  S.ReadBuffer(Result,SizeOf(Integer));
+end;
+
+Function ColReadBoolean(S : TStream) : Boolean;
+
+Var
+  I : Integer;
+
+begin
+  S.ReadBuffer(I,SizeOf(Integer));
+  Result:=(I<>0);
+end;
+
+Function ColReadString(S : TStream) : String;
+
+Var
+  L : Integer;
+
+begin
+  L:=ColReadInteger(S);
+  SetLength(Result,L);
+  If (L>0) then
+    S.ReadBuffer(Result[1],L);
+end;
+
+Function ColReadCurrency(S : TStream) : Currency;
+
+begin
+  S.ReadBuffer(Result,SizeOf(Currency));
+end;
+
+Function ColReadDateTime(S : TStream) : TDateTime;
+
+begin
+  S.ReadBuffer(Result,SizeOf(TDateTime));
+end;
+
+Function ColReadFloat(S : TStream) : Double;
+
+begin
+  S.ReadBuffer(Result,SizeOf(Double));
+end;
+
+{ TStreamCollectionItem }
+
+function TStreamCollectionItem.ReadBoolean(S: TStream): Boolean;
+begin
+  Result:=ColReadBoolean(S);
+end;
+
+function TStreamCollectionItem.ReadCurrency(S: TStream): Currency;
+begin
+  Result:=ColReadCurrency(S);
+end;
+
+function TStreamCollectionItem.ReadDateTime(S: TStream): TDateTime;
+begin
+  Result:=ColReadDateTime(S);
+end;
+
+function TStreamCollectionItem.ReadFloat(S: TStream): Double;
+begin
+  Result:=ColReadFloat(S);
+end;
+
+function TStreamCollectionItem.ReadInteger(S: TStream): Integer;
+begin
+  Result:=ColReadinteger(S);
+end;
+
+function TStreamCollectionItem.ReadString(S: TStream): String;
+begin
+  Result:=ColReadString(S);
+end;
+
+procedure TStreamCollectionItem.WriteBoolean(S: TStream; AValue: Boolean);
+begin
+  ColWriteBoolean(S,AValue);
+end;
+
+procedure TStreamCollectionItem.WriteCurrency(S: TStream;
+  AValue: Currency);
+begin
+  ColWriteCurrency(S,AValue);
+end;
+
+procedure TStreamCollectionItem.WriteDateTime(S: TStream;
+  AValue: TDateTime);
+begin
+  ColWriteDateTime(S,AValue);
+end;
+
+procedure TStreamCollectionItem.WriteFloat(S: TStream; AValue: Double);
+begin
+  ColWriteFloat(S,AValue);
+end;
+
+procedure TStreamCollectionItem.WriteInteger(S: TStream; AValue: Integer);
+begin
+  ColWriteInteger(S,AValue);
+end;
+
+procedure TStreamCollectionItem.WriteString(S: TStream; AValue: String);
+begin
+  ColWriteString(S,AValue);
+end;
+
+{ TStreamCollection }
+
+function TStreamCollection.CurrentStreamVersion: Integer;
+begin
+  Result:=0;
+end;
+
+procedure TStreamCollection.DoLoadFromStream(S: TStream;
+  Streamversion: Integer);
+begin
+  If (Streamversion>CurrentStreamVersion) then
+    Raise EStreamColl.CreateFmt(SErrIllegalStreamVersion,[Streamversion,CurrentStreamVersion]);
+end;
+
+procedure TStreamCollection.DoSaveToStream(S: TStream);
+begin
+  // Do nothing.
+end;
+
+procedure TStreamCollection.LoadFromStream(S: TStream);
+
+Var
+  I,V,C : Integer;
+
+begin
+  FStreaming:=True;
+  Try
+    V:=ReadInteger(S);
+    DoLoadFromStream(S,V);
+    Clear;
+    C:=ReadInteger(S);
+    For I:=1 to C do
+      With Add as TStreamCollectionItem do
+        LoadFromStream(S,V);
+  Finally
+    FStreaming:=False;
+  end;
+end;
+
+function TStreamCollection.ReadBoolean(S: TStream): Boolean;
+begin
+  Result:=ColReadBoolean(S);
+end;
+
+function TStreamCollection.ReadCurrency(S: TStream): Currency;
+begin
+  Result:=ColReadCurrency(S);
+end;
+
+function TStreamCollection.ReadDateTime(S: TStream): TDateTime;
+begin
+  Result:=ColReadDateTime(S);
+end;
+
+function TStreamCollection.ReadFloat(S: TStream): Double;
+begin
+  Result:=ColReadFloat(S);
+end;
+
+function TStreamCollection.ReadInteger(S: TStream): Integer;
+begin
+  Result:=ColReadInteger(S);
+end;
+
+function TStreamCollection.ReadString(S: TStream): String;
+begin
+  Result:=ColReadString(S);
+end;
+
+procedure TStreamCollection.SaveToStream(S: TStream);
+
+Var
+  I : Integer;
+
+begin
+  FStreaming:=True;
+  Try
+    WriteInteger(S,CurrentStreamVersion);
+    DoSaveToStream(S);
+    WriteInteger(S,Count);
+    For I:=0 to Count-1 do
+      With TStreamCollectionItem(Items[i]) do
+        SaveToStream(S);
+  Finally
+    FStreaming:=False;
+  end;
+end;
+
+procedure TStreamCollection.WriteBoolean(S: TStream; AValue: Boolean);
+begin
+  ColWriteBoolean(S,AValue);
+end;
+
+procedure TStreamCollection.WriteCurrency(S: TStream; AValue: Currency);
+begin
+  ColWriteCurrency(S,AValue);
+end;
+
+procedure TStreamCollection.WriteDateTime(S: TStream; AValue: TDateTime);
+begin
+  ColWriteDateTime(S,AValue);
+end;
+
+procedure TStreamCollection.WriteFloat(S: TStream; AValue: Double);
+begin
+  ColWriteFloat(S,AValue);
+end;
+
+procedure TStreamCollection.WriteInteger(S: TStream; AValue: Integer);
+begin
+  ColWriteInteger(S,AValue);
+end;
+
+procedure TStreamCollection.WriteString(S: TStream; AValue: String);
+begin
+  ColWriteString(S,AValue);
+end;
+
+
+end.