{               Copyright 1991 TechnoJock Software, Inc.               }
{                          All Rights Reserved                         }
{                         Restricted by License                        }

{                             Build # 1.00                             }

Unit totLINK;
{$I TOTFLAGS.INC}

{
 Development Notes:

}

INTERFACE

Uses DOS,CRT,
     totSTR;

Const
  NoFiles: string[20] = 'No Files';

Type

tFileInfo = record
     FileName: string[12];
     Attr: byte;
     Time: longint;
     Size: longint;
     LoadID: longint;
end; {tFileInfo}

DLLNodePtr = ^DLLNodeObj;
pDLLNodeOBJ = ^DLLNodeOBJ;
DLLNodeOBJ = Object         {this object is not extensible}
   vNextPtr: DLLNodePtr;
   vPrevPtr: DLLNodePtr;
   vDataPtr: pointer;
   vSize: longint;
   vStatus: byte;   {selectable, selected}
   {methods...}
   procedure FreeData;
   function  NextPtr: DLLNodePtr;
   function  PrevPtr: DLLNodePtr;
   function  GetStatus(BitPos:byte): boolean;
   procedure SetStatus(BitPos:byte;On:boolean);
   function  GetStatusByte: byte;
   procedure SetStatusByte(Val:byte); 
end; {DLLNodeOBJ}

DLLPtr = ^DLLOBJ;
pDLLOBJ = ^DLLOBJ;
DLLOBJ = Object
   vStartNodePtr:  DLLNodePtr;
   vEndNodePtr:    DLLNodePtr;
   vActiveNodePtr: DLLNodePtr;
   vTotalNodes:       longint;
   vActiveNodeNumber: longint;
   vSortID:           shortInt;
   vSortAscending:    boolean;
   vSorted:           boolean;
   vMaxNodeSize :     longint;
   {methods...}
   constructor Init;
   function    Add(var TheData;Size:longint): integer;
   function    Change(Node:DLLNodePtr;var TheData;Size:longint): integer;
   function    InsertBefore(Node:DLLNodePtr;var TheData;Size:longint): integer;
   procedure   Get(var TheData);
   procedure   GetNodeData(Node:DLLNodePtr;Var TheData);
   function    GetNodeDataSize(Node:DLLNodePtr):longint;
   function    GetMaxNodeSize: longint;
   procedure   Advance(Amount:longint);
   procedure   Retreat(Amount:longint);
   function    NodePtr(NodeNumber:longint): DLLNodePtr;
   procedure   Jump(NodeNumber:longint);
   procedure   ShiftActiveNode(NewNode: DLLNodePtr; NodeNumber: longint);
   procedure   DelNode(Node:DLLNodePtr);
   procedure   DelAllStatus(BitPos:byte;On:boolean);
   function    TotalNodes: longint;
   function    ActiveNodeNumber: longint;
   function    ActiveNodePtr: DLLNodePtr;
   function    StartNodePtr: DLLNodePtr;
   function    EndNodePtr: DLLNodePtr;
   procedure   EmptyList;
   procedure   Sort(SortID:shortint;Ascending:boolean);
   function    WrongOrder(Node1,Node2:DLLNodePtr;Asc:boolean): boolean; VIRTUAL;
   procedure   SwapNodes(Node1,Node2:DLLNodePtr);                       VIRTUAL;
   function    GetStr(Node:DLLNodePtr;Start,Finish: longint):string;    VIRTUAL;
   destructor  Done;
end; {DLLOBJ}

StrDLLPtr = ^StrDLLOBJ;
pStrDLLOBJ = ^StrDLLOBJ;
StrDLLOBJ = object (DLLOBJ)
   {methods ...}
   constructor Init;
   function    Add(Str:string): integer;
   function    Change(Node:DLLNodePtr;Str: string): integer;
   function    InsertBefore(Node:DLLNodePtr;Str:string): integer;
   function    WrongOrder(Node1,Node2:DLLNodePtr;Asc:boolean): boolean; VIRTUAL;
   function    GetStr(Node:DLLNodePtr;Start,Finish: longint):string;    VIRTUAL;
   destructor  Done;
end; {StrDLLOBJ}

FileDLLPtr = ^FileDLLOBJ; 
pFileDLLOBJ = ^FileDLLOBJ;
FileDLLOBJ = object (DLLOBJ)
   vFileMasks: string;
   vFileAttrib: word;
   {methods ...}
   constructor Init;
   procedure   FillList;
   procedure   SetFileDetails(FileMasks:string; FileAttrib: word);
   procedure   FillNewMask(FileMasks:string);
   function    GetLongStr(Node:DLLNodePtr):string;
   procedure   GetFileRecord(var FileInfo:tFileInfo; Item:longint);
   function    GetFileMask:string;
   function    WrongOrder(Node1,Node2:DLLNodePtr;Asc:boolean): boolean; VIRTUAL;
   procedure   SwapNodes(Node1,Node2:DLLNodePtr);                       VIRTUAL;
   function    GetStr(Node:DLLNodePtr;Start,Finish: longint):string;    VIRTUAL;
   destructor  Done;
end; {FileDLLOBJ}

function Subdirectory(B : byte):boolean;
function FileAttribs(B:byte):string;
function LongName(Info: tFileInfo):string;
procedure LINKInit;

IMPLEMENTATION
{|||||||||||||||||||||||||||||||||||||||||||||}
{                                             }
{     M i s c.  P r o c s   &   F u n c s     }
{                                             }
{|||||||||||||||||||||||||||||||||||||||||||||}
function Subdirectory(B : byte):boolean;
begin
   Subdirectory := ((B and Directory) = Directory);
end; {Subdirectory}

function FileAttribs(B:byte):string;
var
  S : string;
begin
   S := '    ';
   If ((B and ReadOnly) = Readonly) then
      S[1] := 'R';
   If ((B and Hidden) = Hidden) then
      S[2] := 'H';
   If ((B and SysFile) = SysFile) then
      S[3] := 'S';
   If ((B and Archive) = Archive) then
      S[4] := 'A';
   FileAttribs := S;
end; {FileAttribs}

function LongName(Info: tFileInfo):string;
{}
var 
  DT :datetime;
  S: String;
begin
   S := padleft(Info.FileName,12,' ');
   UnPackTime(Info.Time,DT);
   if Subdirectory(Info.Attr) then                  {add file size}
      S := S + Padright('<DIR>',8,' ')
   else
      S := S + Padright(InttoStr(Info.Size),8,' ');
   S := S + ' ';
   with DT do
   begin
      Case Month of        
         1 : S := S + 'Jan ';
         2 : S := S + 'Feb ';
         3 : S := S + 'Mar ';
         4 : S := S + 'Apr ';
         5 : S := S + 'May ';
         6 : S := S + 'Jun ';
         7 : S := S + 'Jul ';
         8 : S := S + 'Aug ';
         9 : S := S + 'Sep ';
         10: S := S + 'Oct ';
         11: S := S + 'Nov ';
         12: S := S + 'Dec ';
      end; {case}
      S := S + Padright(InttoStr(Day),2,'0')+','+IntToStr(Year)+' ';
      if Hour > 12 then
         S := S + Padright(IntToStr(Hour-12),2,' ')+':'+Padright(IntToStr(min),2,'0')+'p'
      else
         S := S + Padright(IntToStr(Hour),2,' ')+':'+Padright(IntToStr(min),2,'0')+'a';
      S := S + ' '+FileAttribs(Info.Attr);
   end;
   LongName := S;
end; {LongName}
{||||||||||||||||||||||||||||||||||||||||||||||}
{                                              }
{     D L L  N o d e O b j   M E T H O D S     }
{                                              }
{||||||||||||||||||||||||||||||||||||||||||||||}
procedure DLLNodeObj.FreeData;
{}
begin
   if (vDataPtr <> Nil) and (vSize > 0) then
   begin
      Freemem(vDataPtr,vSize);
      vDataPtr := nil;
      vSize:= 0;
   end;
end; {DLLNodeObj.FreeData}

function DLLNodeObj.NextPtr: DLLNodePtr;
{}
begin
   NextPtr := vNextPtr;
end; {DLLNodeOBJ.NextPtr}

function DLLNodeObj.PrevPtr: DLLNodePtr;
{}
begin
   PrevPtr := vPrevPtr;
end; {DLLNodeOBJ.PrevPtr}

function DLLNodeObj.GetStatus(BitPos:byte): boolean;
{}
var TestByte: Byte;
begin
   if BitPos > 7 then
      GetStatus := false
   else
   begin
     Testbyte := vStatus;
     TestByte := TestByte SHR BitPos; {move to end bit}
     GetStatus := odd(TestByte);
   end;
end; {DLLNodeOBJ.GetStatus}

procedure DLLNodeObj.SetStatus(BitPos:byte; On:boolean);
{}
var
   Test : integer;
begin
   if BitPos <= 7 then
   begin
      if On then
      begin
         Test := 1 SHL BitPos;
         vStatus := vStatus or Test
      end
      else
      begin
         Test := not (1 SHL BitPos);
         vStatus := vStatus and Test;
      end;
   end;
end; { DLLNodeObj.SetStatus }

function DLLNodeObj.GetStatusByte: byte;
{}
begin
   GetStatusByte := vStatus;
end; {DLLNodeObj.GetStatusByte}

procedure DLLNodeObj.SetStatusByte(Val:byte);
{}
begin
   vStatus := Val;
end; {DLLNodeObj.SetStatusByte}
{|||||||||||||||||||||||||||||||||||||}
{                                     }
{     D L L O b j   M E T H O D S     }
{                                     }
{|||||||||||||||||||||||||||||||||||||}
constructor DLLOBJ.Init;
{}
begin
   vStartNodePtr := nil;
   vEndNodePtr := nil;
   vActiveNodePtr := nil;
   vTotalNodes := 0;
   vActiveNodeNumber := 0;
   vSortID := 0;
   vSortAscending := true;
   vSorted := true;
   vMaxNodeSize := 0;
end; {DLLOBJ.Init}

function DLLOBJ.Add(var TheData; Size:Longint): integer;
{ Adds node after the ActiveNodePtr, and increments the
  ActiveNodePtr.

  Returns status indicating result of attemp to add.
  Codes:          0      Success
                  1      Not enough memory
                  2      Not enough memory for data
}
var
  Temp: DLLNodePtr;
begin
   if MaxAvail < sizeOf(vStartNodePtr^) then
   begin
      Add := 1;  {not enough memory}
      exit;
   end;
   if vStartNodePtr = nil then
   begin
      getmem(vStartNodePtr,sizeof(vStartNodePtr^));
      vStartNodePtr^.vPrevPtr := nil;
      vActiveNodePtr := vStartNodePtr;
      vActiveNodePtr^.vNextPtr := nil;
      vActiveNodeNumber := 1;
      vEndNodePtr := vActiveNodePtr;
   end
   else
   begin
      if vActiveNodePtr^.vNextPtr = nil then
      begin
         getmem(vActiveNodePtr^.vNextPtr,sizeof(vActiveNodePtr^));
         vActiveNodePtr^.vNextPtr^.vPrevPtr := vActiveNodePtr;
         vActiveNodePtr := vActiveNodePtr^.vNextPtr;
         vActiveNodePtr^.vNextPtr := nil;
         inc(vActiveNodeNumber);
         vEndNodePtr := vActiveNodePtr;
      end
      else  {insert a node}
      begin
         getmem(Temp,sizeof(temp^));
         vActiveNodePtr^.vNextPtr^.vPrevPtr := Temp;
         Temp^.vNextPtr := vActiveNodePtr^.vNextPtr;
         Temp^.vPrevPtr := vActiveNodePtr;
         vActiveNodePtr^.vNextPtr := Temp;
         vActiveNodePtr := Temp;
         inc(vActiveNodeNumber);
      end;
   end;
   inc(vTotalNodes);
   {now add the data to the node data pointer}
   if MemAvail < Size then
   begin
     Add := 2;   {not enough memory for data}
     vActiveNodePtr^.vSize := 0;
     vActiveNodePtr^.vDataPtr := nil;
     exit;
   end;
   if Size > 0 then
   begin
      getmem(vActiveNodePtr^.vDataPtr,Size);
      move(TheData,vActiveNodePtr^.vDataPtr^,Size);
      if Size > vMaxNodeSize then
         vMaxNodeSize := Size;
   end
   else
      vActiveNodePtr^.vDataPtr := nil;
   vActiveNodePtr^.vSize := Size;
   vActiveNodePtr^.vStatus := 0;
   Add := 0;
end; {DLLOBJ.Add}

function DLLOBJ.Change(Node:DLLNodePtr;var TheData; Size:Longint): integer;
{ Returns status indicating result of attemp to add.
  Codes:          0      Success
                  2      Not enough memory for data
                  3      Invalid Node Ptr
}
begin
   if node = nil then
      Change := 3
   else 
   begin
     Node^.FreeData;
     if MaxAvail < Size then
        Change := 2
     else
     begin
         Change := 0;
         getmem(Node^.vDataPtr,Size);
         move(TheData,Node^.vDataPtr^,Size);
         Node^.vSize := Size;
     end;
   end;
end; {DLLOBJ.Change}

function DLLOBJ.InsertBefore(Node:DLLNodePtr;var TheData;Size:longint): integer;
{ Returns status indicating result of attemp to add.
  Codes:          0      Success
                  1      Not enough memory
                  2      Not enough memory for data
                  3      Invalid Node Ptr
}
var
  Temp: DLLNodePtr;
begin
   if node = nil then
      InsertBefore := 3
   else if MaxAvail < sizeOf(Node^) then
      InsertBefore:= 1  {not enough memory}
   else
   begin
      if Node = vStartNodePtr then {add to head of list}
      begin
         getmem(Node^.vPrevPtr,sizeof(Node^));
         Node^.vPrevPtr^.vNextPtr := Node;
         Node := Node^.vPrevPtr;
         Node^.vPrevPtr := nil;
         vStartNodePtr := Node;
      end
      else
      begin
         getmem(Temp,sizeof(Temp^));
         Node^.vPrevPtr^.vNextPtr := Temp;
         Temp^.vPrevPtr := Node^.PrevPtr;
         Node^.vPrevPtr := Temp;
         Temp^.vNextPtr := Node;
         Node := Temp;
      end;
      inc(vTotalNodes);
      vActiveNodeNumber := 1;
      vActiveNodePtr := vStartNodePtr;
      if MemAvail < Size then
      begin
         InsertBefore := 2;   {not enough memory for data}
         Node^.vSize := 0;
         Node^.vDataPtr := nil;
      end
      else
      begin
         if Size > 0 then
         begin
            getmem(Node^.vDataPtr,Size);
            move(TheData,Node^.vDataPtr^,Size);
         end
         else
           Node^.vDataPtr := nil;
         Node^.vSize := Size;
         InsertBefore := 0;
      end;
   end;
end; {DLLOBJ.InsertBefore}

procedure DLLOBJ.Get(Var TheData);
{}
begin
    with vActiveNodePtr^ do
       if vDataPtr <> Nil then
          move(vDataPtr^,TheData,vSize);
end; {DLLOBJ.Get}

procedure DLLOBJ.GetNodeData(Node:DLLNodePtr;Var TheData);
{}
begin
    with Node^ do
       if vDataPtr <> Nil then
          move(vDataPtr^,TheData,vSize);
end; {DLLOBJ.GetNodedata}

function DLLOBJ.GetNodeDataSize(Node:DLLNodePtr):longint;
{}
begin
   with Node^ do
   begin
      if vDataPtr = Nil then
         GetNodeDataSize := 0
      else
         GetNodeDataSize := vSize;
   end;
end; {DLLOBJ.GetNodeDataSize}

function DLLOBJ.GetMaxNodeSize: longint;
{}
begin
   GetMaxNodeSize := vMaxNodeSize;
end; {DLLOBJ.GetMaxNodeSize}

function DLLOBJ.GetStr(Node:DLLNodePtr;Start,Finish:longint): String;
{generic method..usually in descendant object}
var temp: string;
begin
   if Start < 0 then Start := 0;
   if Finish < 0 then Finish := 0;
   {validate Start and Finish Parameters}
   if ((Finish = 0) and (Start = 0))
   or (Start > Finish) then   {get full string}
   begin
      Start := 1;
      Finish := 255;
   end
   else if Finish - Start > 254 then      {too long to fit in string}
      Finish := Start + 254;

   if (Node = Nil)
   or (Node^.vDataPtr = Nil)
   or (Node^.vSize = 0)
   or (Start > Node^.vSize) then
      GetStr := ''
   else
   begin
      if Finish > Node^.vSize then
         Finish := Node^.vSize;
      if Start = 0 then
         inc(Start);
      Move(mem[seg(Node^.vDataPtr^):ofs(Node^.vDataPtr^)+pred(Start)],Temp[1],succ(Finish-Start));
      Temp [0] := chr(succ(Finish-Start));
      GetStr := Temp;
   end;
end; {DLLOBJ.GetStr}

procedure DLLOBJ.Advance(Amount:longint);
{}
var
  I : longint;
begin
   for I := 1 to Amount do
      if vActiveNodePtr^.vNextPtr <> nil then
      begin
          vActiveNodePtr := vActiveNodePtr^.vNextPtr;
          inc(vActiveNodeNumber);
      end;
end; {DLLOBJ.Advance}

procedure DLLOBJ.Retreat(Amount:longint);
{}
var
  I : longint;
begin
   for I := 1 to Amount do
      if vActiveNodePtr^.vPrevPtr <> nil then
      begin
          vActiveNodePtr := vActiveNodePtr^.vPrevPtr;
          dec(vActiveNodeNumber);
      end;
end; {DLLOBJ.Retreat}

procedure DLLOBJ.Jump(NodeNumber:longint);
{}
begin
   if NodeNumber = 1 then
   begin
      vActiveNodePtr := vStartNodePtr;
      vActiveNodeNumber := 1;
   end
   else
   begin
      if NodeNumber < vActiveNodeNumber then
         Retreat(vActiveNodeNumber - NodeNumber)
      else
         Advance(NodeNumber - vActiveNodeNumber);
   end;
end; {DLLOBJ.Jump}

procedure DLLOBJ.ShiftActiveNode(NewNode: DLLNodePtr; NodeNumber: longint);
{}
begin
   vActiveNodePtr := NewNode;
   vActiveNodeNumber := NodeNumber;
end; {DLLOBJ.ShiftActiveNode}

function DLLOBJ.NodePtr(NodeNumber:longint): DLLNodePtr;
{}
var
  StartNode: DLLNodePtr;
  DistanceA,
  DistanceB,
  DistanceC,
  Counter,
  I: LongInt;
  Forwards : boolean;
  Indicator : byte;
begin
   if (NodeNumber < 1) or (NodeNumber > vTotalNodes) then
      NodePtr := nil
   else
   begin
      if NodeNumber = 1 then
         NodePtr := vStartNodePtr
      else if NodeNumber = vTotalNodes then
            NodePtr := vEndNodePtr
      else if NodeNumber = vActiveNodeNumber then
            NodePtr := vActiveNodePtr
      else
      begin
         {check for the nearest node ptr, and jump from there}
         DistanceA := abs(NodeNumber - vActiveNodeNumber);
         DistanceB := NodeNumber;
         DistanceC := vTotalNodes - NodeNumber;
         if DistanceA < DistanceB then
         begin
            if DistanceA < DistanceC then
            begin
               StartNode := vActiveNodePtr;
               Forwards := (vActiveNodeNumber < NodeNumber);
               Counter := DistanceA;
            end
            else
            begin
               StartNode := vEndNodePtr;
               Forwards := false;
               Counter := DistanceC;
            end;
         end
         else      {DA > DB}
         begin
            if DistanceB < DistanceC then
            begin
               StartNode := vStartNodePtr;
               Forwards := true;
               Counter := pred(DistanceB);
            end
            else
            begin
               StartNode := vEndNodePtr;
               Forwards := false;
               Counter := DistanceC;
            end;
         end;
         if Forwards then
            for I := 1 to Counter do
                StartNode := StartNode^.NextPtr
         else
            for I := 1 to Counter do
                StartNode := StartNode^.PrevPtr;
         NodePtr := StartNode;

      end;
  end;
end; {DLLOBJ.NodePtr}

function DLLOBJ.TotalNodes: longint;
{}
begin
    TotalNodes := vTotalNodes;
end;

function DLLOBJ.ActiveNodeNumber: longint;
{}
begin
    ActiveNodeNumber := vActiveNodeNumber;
end;

function DLLOBJ.StartNodePtr: DLLNodePtr;
{}
begin
   StartNodePtr := vStartNodePtr;
end; {DLLOBJ.StartNodePtr}

function DLLOBJ.EndNodePtr: DLLNodePtr;
{}
begin
   EndNodePtr := vEndNodePtr;
end; {DLLOBJ.EndNodePtr}

function DLLOBJ.ActiveNodePtr: DLLNodePtr;
{}
begin
    ActiveNodePtr := vActiveNodePtr;
end; {DLLOBJ.ActiveNodePtr}

(* The following procedure requires only 8 bytes on the 
   stack but damn it's slow!
procedure DLLOBJ.SwapNodes(Node1,Node2:DLLNodePtr);
{swaps the position of two nodes in the tree}
var
   TempPrevPtr,
   TempNextPtr : DLLNodePtr;
begin
   if vStartNodePtr = Node1 then
      vStartNodePtr := Node2
   else if vStartNodePtr = Node2 then
      vStartNodePtr := Node1;
   if vEndNodePtr = Node1 then
      vEndNodePtr := Node2
   else if vEndNodePtr = Node2 then
      vEndNodePtr := Node1;
   if vActiveNodePtr = Node1 then
      vActiveNodePtr := Node2
   else if vActiveNodePtr = Node2 then
      vActiveNodePtr := Node1;
   if (Node1^.vNextPtr = Node2) then  {nodes next to each other}
   begin
      TempNextPtr := Node2^.vNextPtr;
      {move Node2 into Node1's place}
      Node2^.vPrevPtr := Node1^.vPrevPtr;
      Node2^.vNextPtr := Node1;
      if Node2^.vPrevPtr <> nil then
         Node2^.vPrevPtr^.vNextPtr := Node2;
      if Node2^.vNextPtr <> nil then
         Node2^.vNextPtr^.vPrevPtr := Node2;
      {move Node1 into Node2's place}
      Node1^.vPrevPtr := Node2;
      Node1^.vNextPtr := TempNextPtr;
      if Node1^.vNextPtr <> nil then
         Node1^.vNextPtr^.vPrevPtr := Node1;
   end
   else
      if (Node1^.vPrevPtr = Node2) then  {nodes next to each other}
      begin
         TempPrevPtr := Node2^.vPrevPtr;
         {move Node2 into Node1's place}
         Node2^.vPrevPtr := Node1;
         Node2^.vNextPtr := Node1^.vNextPtr;
         if Node2^.vNextPtr <> nil then
            Node2^.vNextPtr^.vPrevPtr := Node2;
         {move Node1 into Node2's place}
         Node1^.vPrevPtr := TempPrevPtr;
         Node1^.vNextPtr := Node2;
         if Node1^.vPrevPtr <> nil then
            Node1^.vPrevPtr^.vNextPtr := Node1;
      end
      else  {the nodes are not adjacent to each other}
      begin
         TempPrevPtr := Node2^.vPrevPtr;
         TempNextPtr := Node2^.vNextPtr;
         {move Node2 into Node1's place}
         Node2^.vPrevPtr := Node1^.vPrevPtr;
         Node2^.vNextPtr := Node1^.vNextPtr;
         if Node2^.vPrevPtr <> nil then
            Node2^.vPrevPtr^.vNextPtr := Node2;
         if Node2^.vNextPtr <> nil then
            Node2^.vNextPtr^.vPrevPtr := Node2;
         {move Node1 into Node2's place}
         Node1^.vPrevPtr := TempPrevPtr;
         Node1^.vNextPtr := TempNextPtr;
         if Node1^.vPrevPtr <> nil then
            Node1^.vPrevPtr^.vNextPtr := Node1;
         if Node1^.vNextPtr <> nil then
            Node1^.vNextPtr^.vPrevPtr := Node1;
      end;
end; {DLLOBJ.SwapNodes}
*)

procedure DLLOBJ.SwapNodes(Node1,Node2:DLLNodePtr);
{}
var 
  Ptr1: pointer;
  Size1,Size2: longint;
  Status1: byte;
  Ecode: integer;
begin
   Status1 := Node1^.GetStatusByte;
   Node1^.SetStatusByte(Node2^.GetStatusByte);
   Node2^.SetStatusByte(Status1);
   Size1 := GetNodeDataSize(Node1);
   if Size1 > 0 then
   begin
      getmem(Ptr1,size1);
      GetNodeData(Node1,Ptr1^);
   end;
   Size2 := GetNodeDataSize(Node2);
   Ecode := Change(Node1,Node2^.vDataPtr^,Size2);
   Ecode := Change(Node2,Ptr1^,Size1);
   if Size1 > 0 then
      freemem(Ptr1,Size1);
end; {DLLOBJ.SwapNodes}

procedure DLLOBJ.DelNode(Node: DLLNodePtr);
{}
begin
   if vActiveNodePtr = Node then   {move active ptr to next entry in list}
   begin
      if vActiveNodePtr^.vNextPtr = nil then
      begin
         dec(vActiveNodeNumber);
         vActiveNodePtr := vActiveNodePtr^.vPrevPtr;
      end
      else
         vActiveNodePtr := vActiveNodePtr^.vNextPtr;
   end;
   if Node = vStartNodePtr then
   begin
      if Node^.vNextPtr = nil then {only node in list}
      begin
         Node^.FreeData;
         Freemem(vStartNodePtr,sizeof(vStartNodePtr^));
         vStartNodePtr := nil;
         vEndNodePtr := nil;
      end
      else
      begin
         vStartNodePtr := vStartNodePtr^.vNextPtr;
         vStartNodePtr^.vPrevPtr := nil;
         Node^.FreeData;
         Freemem(Node,sizeof(Node^));
      end;
   end
   else
   begin
      Node^.vPrevPtr^.vNextPtr := Node^.vNextPtr;
      if Node = vEndNodePtr then
         vEndNodePtr := vEndNodePtr^.vPrevPtr
      else
         Node^.vNextPtr^.vPrevPtr := Node^.vPrevPtr;
      Node^.FreeData;
      Freemem(Node,sizeof(Node^));
   end;
   dec(vTotalNodes);
end; {DLLOBJ.DelNode}

procedure DLLOBJ.DelAllStatus(BitPos:byte;On:boolean);
{}
var
  TempPtr,TempNextPtr: DLLNodePtr;
begin
   if vStartNodePtr <> nil then
   begin
      TempPtr := vStartNodePtr;
      TempNextPtr := TempPtr^.NextPtr;
      while TempNextPtr <> nil do
      begin
         if TempNextPtr^.GetStatus(BitPos) = On then
            DelNode(TempNextPtr)
         else
            TempPtr := TempPtr^.NextPtr;
            TempNextPtr := TempPtr^.NextPtr;
      end;
       if vStartNodePtr^.GetStatus(BitPos) = On then
            DelNode(vStartNodePtr)
   end;
end; {DLLOBJ.DelAllStatus}

function DLLOBJ.WrongOrder(Node1,Node2:DLLNodePtr;Asc:boolean):boolean;
{abstract}
begin
   WrongOrder := false;
end; {DLLOBJ.WrongOrder}

procedure DLLOBJ.Sort(SortID:shortint;Ascending:boolean);
{Shell sort}
var
   I,J,Delta : longint;
   Swapped : boolean;
   Ptr1,Ptr2 : DLLNodePtr;
begin
   if ((vSortID <> SortID) or (vSortAscending <> Ascending) or (vSorted = false))
   and (vTotalNodes >= 2) then
   begin
      vSortID := SortID;
      vSortAscending := Ascending;
      Delta := vTotalNodes div 2;
      repeat
         Repeat
            Swapped := false;
            Ptr1 := vStartNodePtr;
            Ptr2 := Ptr1;
            for I := 1 to Delta do
              Ptr2 := Ptr2^.vNextPtr;
            for I := 1 to vTotalNodes - Delta do
            begin
              if I > 1 then
              begin
                 Ptr1 := Ptr1^.vNextPtr;
                 Ptr2 := Ptr2^.vNextPtr;
              end;
              if WrongOrder(Ptr1,Ptr2,vSortAscending) then
              begin
                 SwapNodes(Ptr1,Ptr2);
                 Swapped := true;
              end;
            end;
         Until (not Swapped);
         Delta := Delta div 2;
      Until Delta = 0;
   end;
   vSorted := true;
end; {DLLOBJ.Sort}

procedure DLLOBJ.EmptyList;
{removes all the memory allocated on the heap by chaining back
 through the list and disposing of each node.}
var TempPtr: DLLNodePtr;
begin
   TempPtr := vEndNodePtr;
   if vEndNodePtr <> nil then
   while TempPtr^.vPrevPtr <> nil do
   begin
      TempPtr^.FreeData;
      TempPtr := TempPtr^.vPrevPtr;
      Freemem(TempPtr^.vNextPtr,sizeof(TempPtr^));
   end;
   if vStartNodePtr <> Nil then
   begin
      vStartNodePtr^.FreeData;
      Freemem(vStartNodePtr,sizeof(vStartNodePtr^));
      vStartNodePtr := Nil;
   end;
   vEndNodePtr := nil;
   vActiveNodePtr := nil;
   vTotalNodes := 0;
   vActiveNodeNumber := 0;
end; {DLLOBJ.EmptyList}

destructor DLLOBJ.Done;
{}
begin
   EmptyList;
end; {of dest DLLOBJ.Done}

{|||||||||||||||||||||||||||||||||||||||||||}
{                                           }
{     S t r D L L O b j   M E T H O D S     }
{                                           }
{|||||||||||||||||||||||||||||||||||||||||||}

{The StrDLLOBJ object is a descendant of the DLLOBJ object, and
 it is designed to specifically manipulate strings}

constructor StrDLLOBJ.Init;
{}
begin
   DLLOBJ.Init;
end; {StrDLLOBJ.Init}

function StrDLLOBJ.Add(Str: string): integer;
{}
var
  Len : byte;
begin
   Len := Length(Str);
   Add := DLLOBJ.Add(Str[1],Len);
end; {StrDLLOBJ.Add}

function StrDLLOBJ.GetStr(Node:DLLNodePtr;Start,Finish:longint): String;
{}
begin
   GetStr := DLLOBJ.GetStr(Node,Start,Finish);
end; {StrDLLOBJ.GetStr}

function StrDLLOBJ.Change(Node:DLLNodePtr;Str: string): integer;
{}
var
  Len:byte;
begin
   Len := length(Str);
   Change := DLLOBJ.Change(Node,Str[1],Len);
end; {StrDLLOBJ.Change}

function StrDLLOBJ.InsertBefore(Node:DLLNodePtr;Str:string): integer;
{}
var
  Len:byte;
begin
   Len := length(Str);
   InsertBefore := DLLOBJ.InsertBefore(Node,Str[1],Len);
end; {StrDLLOBJ.InsertBefore}
 
function StrDLLOBJ.WrongOrder(Node1,Node2:DLLNodePtr;Asc:boolean): boolean;
{}
var S1,S2: string;
begin
(*
   if Asc then
   begin
      GetNodeData(Node1,S1);
      GetNodeData(Node2,S2);
   end
   else
   begin
      GetNodeData(Node1,S2);
      GetNodeData(Node2,S1);
   end;
*)
   if Asc then
   begin
      S1 := GetStr(Node1,1,255);
      S2 := GetStr(Node2,1,255);
   end
   else
   begin
      S1 := GetStr(Node2,1,255);
      S2 := GetStr(Node1,1,255);
   end;
   WrongOrder := (S1 > S2);
end; {StrDLLOBJ.WrongOrder}

destructor StrDLLOBJ.Done;
{}
begin
   DLLOBJ.Done;
end; {StrDLLOBJ.Done}
{|||||||||||||||||||||||||||||||||||||||||||||}
{                                             }
{     F i l e D L L O b j   M E T H O D S     }
{                                             }
{|||||||||||||||||||||||||||||||||||||||||||||}
constructor FileDLLOBJ.Init;
{}
begin
   DLLOBJ.Init;
   vFileMasks := '*.*';
   vFileAttrib := archive + readonly;
end; {FileDLLOBJ.Init}

function FileDLLOBJ.GetStr(Node:DLLNodePtr;Start,Finish: longint):string; 
{ignores Start and Finish parameters - first 13 bytes of the Data is
 the filename.}
var temp : string;
begin
   if (Node = Nil)
   or (Node^.vDataPtr = Nil)
   or (Node^.vSize = 0)  then
      GetStr := ''
   else
   begin
      Move(mem[seg(Node^.vDataPtr^):ofs(Node^.vDataPtr^)],Temp[0],13);
      GetStr := Temp;
   end;
end; {FileDLLOBJ.GetStr}

function FileDLLOBJ.GetLongStr(Node:DLLNodePtr):string;
{}
var Info: tFileInfo;
begin
   if (Node = Nil)
   or (Node^.vDataPtr = Nil)
   or (Node^.vSize = 0)  then
      GetLongStr := ''
   else
   begin
      Move(mem[seg(Node^.vDataPtr^):ofs(Node^.vDataPtr^)],Info,sizeof(Info));
      if Info.FileName = NoFiles then
         GetLongStr := 'No matching files found'
      else
         GetLongStr := LongName(Info);
   end;
end; {FileDLLOBJ.GetLongStr}

procedure FileDLLOBJ.GetFileRecord(var FileInfo:tFileInfo; Item:longint);
{}
var
   Node:DLLNodePtr;
begin
   Node := NodePtr(Item);
   if (Node = Nil)
   or (Node^.vDataPtr = Nil)
   or (Node^.vSize = 0)  then
      FileInfo.FileName := ''
   else
      Move(mem[seg(Node^.vDataPtr^):ofs(Node^.vDataPtr^)],FileInfo,sizeof(FileInfo));
end; {FileDLLOBJ.GetFileRecord}

function FileDLLOBJ.GetFileMask:string;
{}
begin
   GetFileMask := vFileMasks;
end; {FileDLLOBJ.GetFileMask}

procedure FileDLLOBJ.SetFileDetails(FileMasks:string; FileAttrib: word);
{}
begin
   if FileMasks = '' then
      FileMasks := '*.*';
   vFileMasks := FileMasks;
   vFileAttrib := FileAttrib;
end; {FileDLLOBJ.SetFileDetails}

procedure FileDLLOBJ.FillList;
{}
var
  FileDetails: SearchRec;
  FileInfo: tFileInfo;
  TotMasks: byte;
  Mask: string;
  RecSize: byte;
  ECode : integer;

  procedure SaveFileDetails(IsDir:boolean);
  begin
     if FileDetails.Name <> '.' then
     begin
        with FileInfo do
        begin
            FileName := FileDetails.Name;
            Attr := FileDetails.Attr;
            Time := FileDetails.Time;
            Size := FileDetails.Size;
            LoadID := succ(vTotalNodes);
        end;  {with}
        Ecode := Add(FileInfo,RecSize);
        if Ecode = 0 then
          vActiveNodePtr^.SetStatus(1,IsDir);
     end;
  end; {SaveFileDetails}

  procedure ProcessFiles(Attrib:word);
  {}
  var I : integer;
  begin
     for I := 1 to TotMasks do
     begin
        Mask := ExtractWords(I,1,vFileMasks);
        FindFirst(Mask,Attrib,FileDetails);
        while DOSError = 0 do
        begin
           if (Attrib <> Directory) then
              SaveFileDetails(false)
           else if ((Attrib = Directory) and (FileDetails.Attr = Directory)) then
              SaveFileDetails(true);
           FindNext(FileDetails);
        end;
     end;
  end; {ProcessFiles}

begin
   RecSize := sizeof(FileInfo);
   if vStartNodePtr <> Nil then
      EmptyList;
   TotMasks := WordCnt(vFilemasks);
   if ((vFileAttrib and Directory) = Directory) then
   begin
      ProcessFiles(Directory);
      ProcessFiles(vFileAttrib and (Anyfile-Directory-VolumeID));
   end
   else
      ProcessFiles(vFileAttrib);
   if vTotalNodes = 0 then
   begin
      FileInfo.Filename := NoFiles;
      FileInfo.Time := 0;
      Ecode := Add(FileInfo,RecSize);
   end;
   vSorted := (vSortID = 0) and (vSortAscending = true);
end; {FileDLLOBJ.FillList}

procedure FileDLLOBJ.FillNewMask(FileMasks:string);
{}
begin
   SetFileDetails(FileMasks,vFileAttrib);
   FillList;
end; {FileDLLOBJ.FillNewMask}

function FileDLLOBJ.WrongOrder(Node1,Node2:DLLNodePtr;Asc:boolean): boolean;
{}
var F1,F2: tFileInfo;
    P: integer;
    Name1,Name2: string[8];
    Ext1,Ext2: string[3];

    function Name(F:tFileInfo):string;
    {}
    begin
       P := pos('.',F.FileName);
       if P = 0 then
          Name := F.FileName
       else
          Name := copy(F.FileName,1,pred(P));
    end;{Name}

    function Ext(F:tFileInfo):string;
    {}
    begin
       P:= pos('.',F.FileName);
       if P = 0 then
          Ext := ''
       else
          Ext := copy(F.FileName,succ(P),3);
    end; {Ext}

begin
   fillchar(F1,sizeof(F1),#0);
   fillchar(F2,sizeof(F2),#0);
   if Asc then
   begin
      GetNodeData(Node1,F1);
      GetNodeData(Node2,F2);
   end
   else
   begin
      GetNodeData(Node1,F2);
      GetNodeData(Node2,F1);
   end;
   case vSortID of
      0: WrongOrder := (F1.LoadID > F2.LoadID);  {DOS}
      1: begin                                    {NAME}
            Name1 := Name(F1);
            Name2 := Name(F2);
            if (Name1 = Name2) then
               WrongOrder := (Ext(F1) > Ext(F2))
            else
               WrongOrder := (Name1 > Name2);
      end;
      2: begin                                    {EXT}
            Ext1 := Ext(F1);
            Ext2 := Ext(F2);
            if Ext1 = Ext2 then
               WrongOrder := (Name(F1) > Name(F2))
            else
               WrongOrder := (Ext1 > Ext2);
      end;
      3: WrongOrder := (F1.Size > F2.Size);      {SIZE}
      4: WrongOrder := (F1.Time > F2.Time);      {TIME}
      else WrongOrder := false;
   end; {case}
end; {FileDLLOBJ.WrongOrder}

procedure FileDLLOBJ.SwapNodes(Node1,Node2:DLLNodePtr);
{}
var 
  FileInfo: tFileInfo;
  Size: longint;
  Status1: byte;
begin
   Status1 := Node1^.GetStatusByte;
   Node1^.SetStatusByte(Node2^.GetStatusByte);
   Node2^.SetStatusByte(Status1);
   GetNodeData(Node1,FileInfo);
   Size := sizeof(FileInfo);
   Move(Node2^.vDataPtr^,Node1^.vDataPtr^,size);
   Move(FileInfo,Node2^.vDataPtr^,size);
end; {FileDLLOBJ.SwapNodes}

destructor FileDLLOBJ.Done;
{}
begin
   DLLOBJ.Done;
end; {FileDLLOBJ.Done}
{|||||||||||||||||||||||||||||||||||||||||||||||}
{                                               }
{     U N I T   I N I T I A L I Z A T I O N     }
{                                               }
{|||||||||||||||||||||||||||||||||||||||||||||||}
procedure LinkInit;
{initilizes objects and global variables}
begin
end;

{end of unit}
{$ifNDEF OVERLAY}
begin
   LINKInit;
{$ENDif}
end.
