From:	CRDGW2::CRDGW2::MRGATE::"SMTP::CRVAX.SRI.COM::RELAY-INFO-VAX" 12-DEC-1989 11:58
To:	MRGATE::"ARISIA::EVERHART"
Subj:	Retrieve, part 3 of 5

Received: From NAC.NO by GIZMO.SRI.COM with TCP; Tue, 12 DEC 89 07:26:19 PDT
Received: from runix.runit.sintef.no by nac.no (5.61++/1.15) with SMTP 
	id AA14297; Tue, 12 Dec 89 16:24:06 +0100
Received: by runix.runit.sintef.no (norunix.EARN) (1.2/7.8)
	id AA19858;  Tue, 12 Dec 89 16:24:47 +0100
Date: 12 Dec 89 16:23 +0100
From: Jo Torsmyr <j_torsmyr%avh.unit.uninett@nac.no>
To: info-vax@kl.sri.com
Message-Id: <229*j_torsmyr@avh.unit.uninett>
Subject: Retrieve, part 3 of 5

+-+-+-+ Beginning of part 3 +-+-+-+
X`009  RecallArray.
X      `032
X      IMPLICIT OUTPUTS:
X      `032
X`009  NONE
X      `032
X      ROUTINE VALUE:
X      `032
X`009  NONE
X      `032
X      SIDE EFFECTS:
X      `032
X`009  NONE
X      `125
X  `032
X    VAR
X      I`009: CmdInt ;
X    BEGIN
X      ComStr.Length := Ord (RecallArray[ArrayP]) ;
X
X      (* Extract command from the recallbuffer... *)
X      FOR I := 1 TO ComStr.Length DO
X`009  ComStr[I] := RecallArray[Inc (ArrayP, 1)] ;
X
X      (* 1 for the trailing count byte, 1 for the NULL byte *)
X      Inc (ArrayP, 2) ;
X  END (* of PROCEDURE GetNextCommand *) ;
X
X
X  PROCEDURE SkipCommands (VAR ArrayP`009: BufferInt ;
X`009`009`009  VAR NumOfCmds`009: $UBYTE ;
X`009`009`009  SkipCmdNr`009: $UBYTE) ;
X  `032
X      `123
X      FUNCTIONAL DESCRIPTION:`032
X      `032
X`009  SkipCommands skips the number of commands specified by SkipCmdNr.
X`009  It it used when recalling a specific command number and when listing
X`009  the content of the recallbuffer.
X`009  Initially, ArrayP points to the trailing length byte of the last
X`009  command in the recallbuffer. When finished, it points to the
X`009  trailing length byte of command number SkipCmdNr.
X
X      `032
X      FORMAL PARAMETERS:`032
X      `032
X`009  ArrayP - The pointer to the recallbuffer.
X`009  NumOfCmds - The number of commands actually skipped.
X`009  SkipCmdNr - The number of commands to skip.
X      `032
X      IMPLICIT INPUTS:
X      `032
X`009  LogEndPointer and RecallArray.
X      `032
X      IMPLICIT OUTPUTS:
X      `032
X`009  NONE
X      `032
X      ROUTINE VALUE:
X      `032
X`009  NONE
X      `032
X      SIDE EFFECTS:
X      `032
X`009  NONE
X      `125
X  `032
X    BEGIN
X      NumOfCmds`009:= 1 ;
X
X      WHILE NOT ((NumOfCmds = SkipCmdNr) OR`009    (* Command found *)
X`009`009 (RecallArray[ArrayP] = Null)) DO   (* No more commands *)
X`009    BEGIN
X`009      (* Step past one command, ArrayP points to trailing length byte *)
X`009      Inc (ArrayP, - Ord (RecallArray[ArrayP]) -3) ;
X`009      NumOfCmds := NumOfCmds + 1 ;
X`009    END (* of WHILE *) ;
X  END (* of PROCEDURE SkipCommands *) ;
X
X
X  FUNCTION MatchStr (Src1 : VARYING [A] OF CHAR ;
X`009`009     Src2 : VARYING [B] OF CHAR) : BOOLEAN ;
X  `032
X      `123
X      FUNCTIONAL DESCRIPTION:`032
X      `032
X`009  MatchStr compares two strings for equality, upper/lowercase are
X`009  considered equal. Str$Case_Blind_Compare returns 0 when both strings
X`009  are the same with blank fill for shorter string.
X
X      `032
X      FORMAL PARAMETERS:`032
X      `032
X`009  Src1 - A command from the recallbuffer.
X`009  Src2 - The searchparameter.
X      `032
X      IMPLICIT INPUTS:
X      `032
X`009  NONE
X      `032
X      IMPLICIT OUTPUTS:
X      `032
X`009  NONE
X      `032
X      ROUTINE VALUE:
X      `032
X`009  Returns TRUE if "equal", FALSE otherwise.
X      `032
X      SIDE EFFECTS:
X      `032
X`009  NONE
X      `125
X  `032
X    BEGIN
X      IF (Src2.Length > Src1.Length) THEN
X`009 MatchStr := FALSE
X      ELSE
X`009 MatchStr := Str$Case_Blind_Compare (SubStr (Src1, 1, Src2.Length),
X`009`009`009`009`009     Src2) = 0 ;
X  END (* of FUNCTION MatchStr *) ;
X
X
X  PROCEDURE DeleteCommand ;
X  `032
X      `123
X      FUNCTIONAL DESCRIPTION:`032
X      `032
X`009  DeleteCommand runs in executive mode. The command bytes and length
X`009  bytes are zeroed out and the physical pointer is updated.
X`009  Initially, LogEndPointer is assumed to point to the command's
X`009  trailing length byte. When finished, LogEndPointer points to the
X`009  NULL byte next to the command's leading length byte.
X
X`009  Illustration of the movement of the bufferpointers:
X
X`009  -------------------------------------
X`009  ... `124 0 `124 3 `124 D `124 I `124 R `124 3 `124 0 `124 ...
X`009  -------------------------------------
X`009`009`094`009`009    `094`009`094
X`009`009PhysEndPointer <---`009PhysEndPointer
X`009`009LogEndPointer  <--- LogEndPointer      `032
X
X
X      FORMAL PARAMETERS:`032
X      `032
X`009  NONE
X      `032
X      IMPLICIT INPUTS:
X      `032
X`009  NONE
X      `032
X      IMPLICIT OUTPUTS:
X      `032
X`009  RecallArray, LogEndPointer and PhysEndPointer.
X      `032
X      ROUTINE VALUE:
X      `032
X`009  NONE
X      `032
X      SIDE EFFECTS:
X      `032
X`009  NONE
X      `125
X  `032
X    VAR
X      I : CmdInt ;
X    BEGIN
X      (* Clear trailing length byte *)
X      RecallArray[LogEndPointer] := 0 ;
X      (* Clear command and leading length byte *)
X      FOR I := 1 TO RetrCmdLen + 1 DO
X`009  RecallArray[Inc (LogEndPointer, -1)] := 0 ;
X      (* Position LogEndPointer at the NULL byte *)
X      Inc (LogEndPointer, -1) ;
X      (* Update PhysEndPointer, very important indeed *)
X      PhysEndPointer := PhysStartAddr + LogEndPointer ;
X  END (* of PROCEDURE DeleteCommand *) ;
X
X
X  [INITIALIZE]
X  PROCEDURE DelRetrCmd ;
X  `032
X      `123
X      FUNCTIONAL DESCRIPTION:`032
X      `032
X`009  DelRetrCmd deletes the command used to execute this program! That
X`009  is, e.g. $ RETRIEVE/LIST. This procedure is executed before the
X`009  main program due to the INITALIZE attribute. Routine DeleteCommand
X`009  is called and executed in executive mode.
X
X      `032
X      FORMAL PARAMETERS:`032
X      `032
X`009  NONE
X      `032
X      IMPLICIT INPUTS:
X      `032
X`009  PhysEndPointer and RecallArray.
X      `032
X      IMPLICIT OUTPUTS:
X      `032
X`009  LogEndPointer and RetrCmdLen.
X      `032
X      ROUTINE VALUE:
X      `032
X`009  NONE
X      `032
X      SIDE EFFECTS:
X      `032
X`009  NONE
X      `125
X  `032
X    BEGIN
X      (* Make LogEndPointer point to the NULL byte *)
X      LogEndPointer := PhysEndPointer - PhysStartAddr ;
X      (* The length of the RETRIEVE command which invoked this program *)
X      RetrCmdLen := RecallArray[Inc (LogEndPointer, -1)] ;
X      $CmExec (%IMMED DeleteCommand) ;
X  END (* of PROCEDURE DelRetrCmd *) ;
X
X
X  PROCEDURE RestoreBuffer ;
X    `032
X      `123
X      FUNCTIONAL DESCRIPTION:`032
X
X`009  RestoreBuffer runs in executive mode. RestoreBuffer actually
X`009  restores the saved commands from the file. LoadPost contains the
X`009  information from the file.
X
X`009`032
X      FORMAL PARAMETERS:`032
X`009`032
X`009  NONE
X`009`032
X      IMPLICIT INPUTS:
X`009`032
X`009  LoadPost.
X`009`032
X      IMPLICIT OUTPUTS:
X`009`032
X`009  RecallArray and PhysEndPointer.
X`009`032
X      ROUTINE VALUE:
X`009`032
X`009  NONE
X
X      SIDE EFFECTS:
X`009`032
X`009  NONE
X      `125
X    `032
X    BEGIN
X      RecallArray`009:= LoadPost.RecArr ;
X      PhysEndPointer`009:= LoadPost.RecP ;
X  END (* of PROCEDURE RestoreBuffer *) ;
X
X
X  PROCEDURE LoadCmds ;
X  `032
X      `123
X      FUNCTIONAL DESCRIPTION:`032
X      `032
X`009  LoadCmds loads command from a file, the file must be sequential.
X`009  Default file specification is Sys$Login:Retrieve.Dat. Any errors
X`009  during Open is signaled. The value of the pointer is checked before
X`009  assigned, so that we don't get confused later...
X
X      `032
X      FORMAL PARAMETERS:`032
X      `032
X`009  NONE
X      `032
X      IMPLICIT INPUTS:
X      `032
X`009  PhysEndPointer.
X      `032
X      IMPLICIT OUTPUTS:
X      `032
X`009  LogEndPointer and LoadPost.
X      `032
X      ROUTINE VALUE:
X      `032
X`009  NONE
X      `032
X      SIDE EFFECTS:
X      `032
X`009  NONE
X      `125
X  `032
X    VAR
X      CmdFile`009: FILE OF RecallRecord ;    (* Local file variable *)
X      FileName`009: VarCmdType ;`009`009    (* The name of the file *)
X
X    BEGIN (* of PROCEDURE LoadCmds *)
X      Cli$Get_Value (Cli$_Restore, FileName) ;
X      Open (CmdFile, FileName, History := Old, Error := Continue) ;
X      IF (Status (CmdFile) = Pas$K_Success) THEN (* => No error *)
X`009 BEGIN
X`009   ReSet (CmdFile) ;
X`009   Read (CmdFile, LoadPost) ;
X`009   Close (CmdFile) ;
X`009   IF NOT ((LoadPost.RecP > PhysEndAddr) OR
X`009`009   (LoadPost.RecP < PhysStartAddr)) THEN (* Careful, careful *)
X`009      BEGIN
X`009`009$CmExec (%IMMED RestoreBuffer) ;
X`009`009LogEndPointer := PhysEndPointer - PhysStartAddr ;
X`009`009IF Log THEN
X`009`009   Lib$Signal (RETR_ResFroFil, 1, %STDESCR (FileName)) ;
X`009      END (* of IF *)
X`009   ELSE (* => Error in file, pointer out of range...*)
X`009      Lib$Signal (RETR_IllPhyVal, 1, LoadPost.RecP) ;
X`009 END (* of IF *)
X      ELSE (* Notice user *)
X`009 Lib$Signal (RETR_ErrDurOpen, 1, %STDESCR (FileName)) ;
X  END (* of PROCEDURE LoadCmds *) ;
X
X
X  PROCEDURE SaveCmds ;
X  `032
X      `123
X      FUNCTIONAL DESCRIPTION:`032
X      `032
X`009  SaveCmds saves the content of the recallbuffer on a specified file.
X`009  Sys$Login:Retrieve.Dat is the default file specification. Note that
X`009  if the file already exists, a new version is created.
X
X      `032
X      FORMAL PARAMETERS:`032
X      `032
X`009  NONE
X      `032
X      IMPLICIT INPUTS:
X      `032
X`009  PhysEndPointer and RecallArray.
X      `032
X      IMPLICIT OUTPUTS:
X      `032
X`009  NONE
X      `032
X      ROUTINE VALUE:
X      `032
X`009  NONE
X      `032
X      SIDE EFFECTS:
X      `032
X`009  NONE
X      `125
X  `032
X    VAR
X      CmdFile`009: FILE OF RecallRecord ;    (* Local file variable *)
X      FileName`009: VarCmdType ;`009`009    (* The name of the file *)
X      SavePost`009: RecallRecord ;`009    (* Record to be written to file *)
X    BEGIN
X      Cli$Get_Value (Cli$_Save, FileName) ;
X      Open (CmdFile, FileName, History := New, Error := Continue) ;
X      IF (Status (CmdFile) = Pas$K_Success) THEN
X`009 BEGIN
X`009   SavePost.RecArr  := RecallArray ;
X`009   SavePost.RecP    := PhysEndPointer ;
X`009   ReWrite (CmdFile) ;
X`009   Write (CmdFile, SavePost) ;
X`009   Close (CmdFile) ;
X`009   IF Log THEN
X`009      Lib$Signal (RETR_SavOnFil, 1, %STDESCR (FileName)) ;
X`009 END (* of IF *)
X      ELSE
X`009 Lib$Signal (RETR_ErrDurOpen, 1, %STDESCR (FileName)) ;
X  END (* of PROCEDURE SaveCmds *) ;
X
X
X  PROCEDURE InsRetrCmd ;
X  `032
X      `123
X      FUNCTIONAL DESCRIPTION:`032
X      `032
X`009  InsRetrCmd runs in executive mode. InsRetrCmd inserts the new
X`009  command, provided that it is not equal to the last command in the
X`009  buffer. Any resulting partial command is then zeroed out.
X`009  Initially, LogEndPointer is assumed to point to the NULL byte next
X`009  to the leading length byte. When finished, PhysEndPointer will point
X`009  to the NULL byte next to the trailing length byte. LogEndPointers
X`009  value is no longer of interest.
X
X`009  Illustration of the movement of the bufferpointers:
X
X`009  -------------------------------------
X`009  ... `124 0 `124 3 `124 D `124 I `124 R `124 3 `124 0 `124 ...
X`009  -------------------------------------
X`009`009`094`009`009`009`094
X`009`009PhysEndPointer`009--->`009PhysEndPointer
X`009`009LogEndPointer`009--->`009...LogEndPointer
X
X      FORMAL PARAMETERS:`032
X      `032
X`009  NONE
X      `032
X      IMPLICIT INPUTS:
X      `032
X`009  GlobalCmd.
X      `032
X      IMPLICIT OUTPUTS:
X      `032
X`009  RecallArray, LogEndPointer and PhysEndPointer.
X      `032
X      ROUTINE VALUE:
X      `032
X`009  NONE
X      `032
X      SIDE EFFECTS:
X      `032
X`009  NONE
X      `125
X  `032
X    VAR
X      I : CmdInt ;
X    BEGIN
X      (* Insert leading length byte *)
X      RecallArray[Inc (LogEndPointer, 1)] := GlobalCmd.Length ;
X
X      (* Insert command *)
X      FOR I := 1 TO GlobalCmd.Length DO
X`009  RecallArray[Inc (LogEndPointer, 1)] := GlobalCmd[I] ;
X
X      (* Insert trailing length byte *)
X      RecallArray[Inc (LogEndPointer, 1)] := GlobalCmd.Length ;
X
X      (* PhysEndPointer now points to the NULL byte *)
X      PhysEndPointer := PhysStartAddr + Inc (LogEndPointer, 1) ;
X
X      (* Delete the possible overwritten command.... *)
X      REPEAT
X`009RecallArray[LogEndPointer] := 0 ;
X      UNTIL (RecallArray[Inc (LogEndPointer, 1)] = Null) ;
X  END (* of PROCEDURE InsRetrCmd *) ;
X
X
X  PROCEDURE LineEditor (IniStr, PromptStr   : VarCmdType ;
X`009`009`009ArrayP`009`009    : BufferInt ;
X`009`009`009VAR DstStr`009    : VarCmdType) ;
X
X      `123
X      FUNCTIONAL DESCRIPTION:`032
X      `032
X`009  LineEditor enables the use of the cursor keys to step through the
X`009  RecallBuffer. It imitates the line editing provided by DCL.
X`009  It it unfortunately not able to continue a read operation on the
X`009  next line if the previous line is terminated with '-'. Linewrapping
X`009  is however enabled (if the terminal is in wrap mode).
X`009  Maximum length of a command is 251 characters.
X`009  If you press the Up-arrow or Down-arrow key and the current command
X`009  string extends over several lines, the last line is deleted, and the
X`009  new command string is displayed, starting on that line.
X`009  The pointer to the recallbuffer (ArrayP) points to the NULL byte
X`009  next to the current command's (the command which is displayed)
X`009  leading length byte.
X
X`009  Illustration of how the commands are recalled:
X
X
X    UP  :     ArrayP`009   <---`009      ArrayP
X`009    -----------------------------------------------------------------
V`009    `124 0 `124 3 `124 M `124 A `124 I `124 3 `124 0 `124 2 `124 S `124 U
X `124 2 `124 0 `124 1 `124 D `124 1 `124 0 `124
X`009    -----------------------------------------------------------------
X`009`009`009`009      `094`009`009`009  `094`009`009  `094
X    DOWN:`009`009`009      ArrayP`009--->`009  ArrayP
X`009`009`009`009`009`009`009  DummyP   --->`009  DummyP
X
X`009  Current command   : SU
X`009  Next command`009    : D
X`009  Previous command  : MAI
X
X
X`009  Description of the legal terminators:
X
X`009  UP: When the Up-arrow key is pressed, ArrayP is decremented to point
X`009      to the trailing length byte of the previous command (the one that
X`009      is to be displayed). The command is extracted and ArrayP now
X`009      points to the NULL byte next to the leading length byte of the
X`009      command to display.
X`009      If ArrayP is equal to LogEndPointer, we must also check whether
X`009      DownArrowEnd is set. If it is, the previous command is displayed.
X`009      If it isn't, there are no more commands to display and nothing
X`009      happens.
X      `032
X`009  DOWN: When the Down-arrow key is pressed, ArrayP is incremented to
X`009`009point to the leading length byte of the current command. Since
X`009`009this is the length byte of the command currently being
X`009`009displayed, ArrayP is incremented to point to the NULL byte next
X`009`009to the trailing length byte of this command.
X`009`009DummyP is set equal to ArrayP and incremented to point to the
X`009`009leading length byte of the next command (the one to be
X`009`009displayed). ArrayP is not used to do this because if the
X`009`009Up-arrow key is pressed, ArrayP is already in right position.
X`009`009The value of DummyP is not of any interest, it is just used
-+-+-+-+-+ End of part 3 +-+-+-+-+-

