
{*******************************************************}
{                                                       }
{       Turbo Pascal Version 7.0                        }
{       Turbo Vision Unit                               }
{                                                       }
{       Copyright (c) 1992 Borland International        }
{                                                       }
{*******************************************************}

unit HistList;

{$O+,F+,X+,I-,S-}

{****************************************************************************
   History buffer structure:

    Byte Byte String          Byte Byte String
    +-------------------------+-------------------------+--...--+
    | 0 | Id | History string | 0 | Id | History string |       |
    +-------------------------+-------------------------+--...--+

 ***************************************************************************}

interface

uses Objects;

const
  HistoryBlock: Pointer = nil;
  HistorySize: Word = 1024;
  HistoryUsed: Word = 0;

procedure HistoryAdd(Id: Byte; const Str: String);
function HistoryCount(Id: Byte): Word;
function HistoryStr(Id: Byte; Index: Integer): String;
procedure ClearHistory;

procedure InitHistory;
procedure DoneHistory;

procedure StoreHistory(var S: TStream);
procedure LoadHistory(var S: TStream);

implementation

var
  CurId: Byte;
  CurString: PString;

{ Advance CurString to next string with an ID of CurId }

procedure AdvanceStringPointer; near; assembler;
asm
	PUSH	DS
        MOV     CX,HistoryUsed
        MOV     BL,CurId
	LDS	SI,CurString
        MOV     DX,DS
        MOV     AX,DS
        OR      AX,SI
        JZ      @@3
        CLD
        JMP	@@2
@@1:	LODSW
	CMP	AH,BL { BL = CurId }
        JE	@@3
@@2:    LODSB
        XOR	AH,AH
        ADD	SI,AX
	CMP	SI,CX { CX = HistoryUsed }
        JB	@@1
        XOR	SI,SI
        MOV	DX,SI
@@3:    POP	DS
	MOV	CurString.Word[0],SI
	MOV	CurString.Word[2],DX
end;

{ Deletes the current string from the table }

procedure DeleteString; near; assembler;
asm
	PUSH	DS
        MOV	CX,HistoryUsed
        CLD
        LES	DI,CurString
        MOV     SI,DI
        DEC	DI
        DEC	DI
        PUSH	ES
        POP	DS
        MOV	AL,BYTE PTR [SI]
        XOR	AH,AH
        INC     AX
	ADD	SI,AX
        SUB	CX,SI
        REP	MOVSB
        POP	DS
	MOV	HistoryUsed,DI
end;

{ Insert a string into the table }

procedure InsertString(Id: Byte; const Str: String); near; assembler;
asm
	PUSH	DS
        STD

        { Position ES:DI to the end the buffer  }
        {          ES:DX to beginning of buffer }
        LES	DX,HistoryBlock
        MOV	DI,HistoryUsed
	LDS	SI,Str
	MOV	BL,[SI]
        INC	BL
        INC	BL
        INC     BL
        XOR	BH,BH
        POP     DS
        PUSH    DS
@@1:    MOV     AX,DI
        ADD	AX,BX
	SUB	AX,DX { DX = HistoryBlock.Word[0] }
        CMP	AX,HistorySize
        JB	@@2

        { Drop the last string off the end of the list }
        DEC 	DI
        XOR	AL,AL
        MOV	CX,0FFFFH
        REPNE	SCASB
        INC	DI
        JMP     @@1

        { Move the table down the size of the string }
@@2:	MOV	SI,DI
	ADD	DI,BX
        MOV     HistoryUsed,DI
        PUSH	ES
	POP	DS
        MOV	CX,SI
        SUB	CX,DX { DX = HistoryBlock.Word[0] }
	REP	MOVSB

        { Copy the string into the position }
        CLD
        MOV     DI,DX { DX = HistoryBlock.Word[0] }
        INC     DI
        MOV	AH,Id
        XOR	AL,AL
	STOSW
        LDS	SI,Str
        LODSB
        STOSB
        MOV	CL,AL
        XOR	CH,CH
        REP	MOVSB

        POP	DS
end;

procedure StartId(Id: Byte); near;
begin
  CurId := Id;
  CurString := HistoryBlock;
end;

function HistoryCount(Id: Byte): Word;
var
  Count: Word;
begin
  StartId(Id);
  Count := 0;
  AdvanceStringPointer;
  while CurString <> nil do
  begin
    Inc(Count);
    AdvanceStringPointer;
  end;
  HistoryCount := Count;
end;

procedure HistoryAdd(Id: Byte; const Str: String);
begin
  if Str = '' then Exit;

  StartId(Id);

  { Delete duplicates }
  AdvanceStringPointer;
  while CurString <> nil do
  begin
    if Str = CurString^ then DeleteString;
    AdvanceStringPointer;
  end;

  InsertString(Id, Str);
end;

function HistoryStr(Id: Byte; Index: Integer): String;
var
  I: Integer;
begin
  StartId(Id);
  for I := 0 to Index do AdvanceStringPointer;
  if CurString <> nil then
    HistoryStr := CurString^ else
    HistoryStr := '';
end;

procedure ClearHistory;
begin
  PChar(HistoryBlock)^ := #0;
  HistoryUsed := PtrRec(HistoryBlock).Ofs + 1;
end;

procedure StoreHistory(var S: TStream);
var
  Size: Word;
begin
  Size := HistoryUsed - PtrRec(HistoryBlock).Ofs;
  S.Write(Size, SizeOf(Word));
  S.Write(HistoryBlock^, Size);
end;

procedure LoadHistory(var S: TStream);
var
  Size: Word;
begin
  S.Read(Size, SizeOf(Word));
  S.Read(HistoryBlock^, Size);
  HistoryUsed := PtrRec(HistoryBlock).Ofs + Size;
end;

procedure InitHistory;
begin
  GetMem(HistoryBlock, HistorySize);
  ClearHistory;
end;

procedure DoneHistory;
begin
  FreeMem(HistoryBlock, HistorySize);
end;

end.
