module KermitSend ;

(* 29-Nov-83 Allow eight bit file transfer (c.f. sopen call) [pgt001] *)
 
 
EXPORTS
 
PROCEDURE SendPacket;
PROCEDURE SendACK((* Using *) n:Integer); (* send ACK packet *)
PROCEDURE SendNAK((* Using *) n:Integer); (* send NAK packet *)
PROCEDURE SendSwitch;
 
 
 

PRIVATE
 
IMPORTS KermitGlobals   FROM KermitGlobals ;
IMPORTS KermitUtils     FROM KermitUtils ;
IMPORTS Stdio           FROM Stdio ;
IMPORTS KermitError     FROM KermitError ;
IMPORTS KermitRecv      FROM KermitRecv ;    (* for receiving ACKs and NAKs *)
IMPORTS UtilProgress    FROM UtilProgress ;
IMPORTS Sleep           FROM Sleep ;
 

{$RANGE-}    (* Range checks off   16-Jan-84 *)




VAR
   DataSendCount: Integer ; (* counter for progress *)
 
 
PROCEDURE PutOut( p : Ppack); (* Output Packet *)
   (* Use direct calls to XmtChar to send the characters -pt*)
   VAR
      i : Integer;
   BEGIN
      IF (NumPad > 0) THEN
         FOR i := 1 TO NumPad DO
            XmtChar( Chr(PadChar) );
      WITH Buf[p] DO
         BEGIN
            XmtChar( Chr(mark) );
            XmtChar( Chr(count) );
            XmtChar( Chr(seq) );
            XmtChar( Chr(ptype) );
            FOR i := 1 TO ilength(data) DO
               XmtChar( Chr(data[i]) );
         END;
   END;
 
 
PROCEDURE ReSendPacket;
   (* re -sends previous packet *)
   BEGIN
      NumSendPacks := NumSendPacks+1;
      ChInPack := ChInPack + NumPad + UnChar(Buf[LastPacket].count) + 3 ;
      IF Debug
      THEN DebugPacket('Re-Sending: ',LastPacket);
      PutOut(LastPacket);
   END;
 
PROCEDURE SendPacket;
 
   (* expects count as length of data portion *)
   (* and seq as number of packet *)
   (* builds & sends packet *)
   VAR
      i,len,chksum : Integer;
      temp : Ppack;
   BEGIN
      IF (NumTry <> 1) AND (RunType = Transmit) THEN
         ReSendPacket
      ELSE
         BEGIN
            WITH Buf[ThisPacket] DO
               BEGIN
                  mark :=SOH;               (* mark *)
                  len := count;             (* save length *)
                  count := MakeChar(len+3); (* count = 3+length of data *)
                  seq := MakeChar(seq);     (* seq number *)
                  chksum := count + seq + ptype;
                  IF (len > 0) THEN      (* is there data ? *)
                     FOR i:= 1 TO len DO
                        chksum := chksum + data[i];       (* loop for data *)
                  chksum := CheckFunction(chksum);  (* calculate  checksum *)
                  data[len+1] := MakeChar(chksum);  (* make printable & output *)
                  data[len+2] := SendEOL;                    (* EOL *)
                  data[len+3] := ENDSTR;
               END;
 
            NumSendPacks := NumSendPacks+1;
            IF Debug
            THEN DebugPacket('Sending: ',ThisPacket);
            PutOut(ThisPacket);
 
            IF (RunType = Transmit) THEN
               BEGIN
                  ChInPack := ChInPack + NumPad + len + 6;
                  temp := LastPacket;
                  LastPacket := ThisPacket;
                  ThisPacket := temp;
               END;
         END
 
   END;
 
PROCEDURE SendACK((* Using *) n:Integer); (* send ACK packet *)
   BEGIN
      WITH Buf[ThisPacket] DO
         BEGIN
            count := 0;
            seq := n;
            ptype := TYPEY;
         END;
      SendPacket;
      NumACK := NumACK+1;
   END;
 
PROCEDURE SendNAK((* Using *) n:Integer); (* send NAK packet *)
   BEGIN
      WITH Buf[ThisPacket] DO
         BEGIN
            count := 0;
            seq := n;
            ptype := TYPEN;
         END;
      SendPacket;
      NumNAK := NumNAK+1;
   END;
 
 
 
PROCEDURE GetData((* Returning *)   VAR newstate:KermitStates);
   (* get data from file into ThisPacket *)
   VAR
      (* and return next state - data &  EOF *)
      x,c : CharBytes;
      i: Integer;
   BEGIN
      IF (NumTry = 1) THEN
         BEGIN
            i := 1;
            x := ENDSTR;
            WITH Buf[ThisPacket] DO
               BEGIN
                  WHILE (i< SizeSend - 8 ) AND (x <> ENDFILE)
                  (* leave room for quote  & NEWLINE *)
                  DO
                     BEGIN
                        x := getcf(c,DiskFile);
                        IF (x <> ENDFILE) THEN
                           IF IsControl(x) OR (x = SendQuote) THEN
                              BEGIN           (* control char -- quote *)
                                 IF (x = LF) THEN  (* use proper EOL *)
                                   BEGIN
                                      data[i] := SendQuote;
                                      i := i+1;
                                      data[i] := Ctl(CR);
                                      i := i+1;
                                      (* LF will sent below *)
                                   END;
                                 data[i] := SendQuote;
                                 i := i+1;
                                 IF (x <> SendQuote) THEN  data[i] := Ctl(x)
                                 ELSE  data[i] := SendQuote;
                              END
                           ELSE               (* regular char *)
                              data[i] := x;
 
                        IF (x <> ENDFILE) THEN
                           BEGIN
                              i := i+1;    (* increase count for next char *)
                              ChInFile := ChInFile + 1 ;
                           END;
                     END;
 
                  data[i] := ENDSTR;   (* to terminate string *)
 
                  count := i -1;       (* length *)
                  seq := n;
                  ptype := TYPED;
 
                  IF (x = ENDFILE) THEN
                     BEGIN
                        newstate := EOFile;
                        Sclose(DiskFile);
                        DiskFile := StdIOError;
                     END
                  ELSE
                     newstate := FileData;
                  SaveState := newstate;        (* save state *)
               END
         END
      ELSE
         newstate := SaveState;        (* get old state *)
   END;
 
FUNCTION GetNextFile: (* Returning *) Boolean;
   (* get next file to send in ThisPacket *)
   (* returns true if no more *)
   (*         ----    --      -pt*)
   VAR
      result: Boolean;
   BEGIN
      result := True;
      IF (NumTry = 1) THEN
         WITH Buf[ThisPacket] DO
            BEGIN
               IF GetArgument(data) THEN
                  BEGIN            (* open file  *)
                     IF Exists(data) THEN
                        BEGIN
                           (* Initialise counter for each file to be sent *)
                           DataSendCount := 0 ;

                           IF EightBitFile THEN  (* [pgt001] *)
                              DiskFile := Sopen(data,StdIO8Read)
                           ELSE
                              DiskFile := Sopen(data,StdIORead);

                           count := ilength(data);
                           ChInFile := ChInFile + count ;
                           seq := n;
                           ptype := TYPEF;
                           Write('[Sending ');
                           PutStr(data,stdout);
                           Writeln(']') ;
                           IF (DiskFile <= StdIOError) THEN
                              ErrorMsg('?Can''t open file');
                           result := False;
                        END
                     ELSE (* file does not exist *)
                        BEGIN
                           ErrorMsg('?Can''t find file: ') ;
                           ErrorStr( data ) ;
                           result := True  (* I.e. fail: state -> abort *)
                        END
                  END;
            END
      ELSE
         result := False; (* for saved packet *)
      GetNextFile := result;
   END;
 
PROCEDURE SendFile; (* send file name packet *)
   BEGIN
      Verbose( 'Sending ');
      IF (NumTry > MaxTry) THEN
         BEGIN
            ErrorMsg ('Send file - Too Many');
            State := Abort;      (* too many tries, abort *)
         END
      ELSE
         BEGIN
            NumTry := NumTry+1;
            IF GetNextFile THEN
               BEGIN
                  State := Break;
                  NumTry := 0;
               END
            ELSE
               BEGIN
                  IF Verbosity THEN
                     IF (NumTry = 1)
                     THEN ErrorStr(Buf[ThisPacket].data)
                     ELSE ErrorStr(Buf[LastPacket].data);
                  SendPacket;     (* send this packet *)
                  IF ReceiveACK THEN
                     BEGIN
                        State := FileData;
                        NumTry := 0;
                        n := (n+1) MOD 64;
                     END
               END;
         END;
   END;
 
PROCEDURE SendData;  (* send file data packets *)
   VAR
      newstate: KermitStates;
   BEGIN
      IF (Land(DataSendCount, #03) = 0) THEN
        WITH OpenList[DiskFile] DO
         StreamProgress( FileVar ) ;
      DataSendCount := DataSendCount + 1 ;  (* next "SendData" *)

      IF (NumTry > MaxTry) THEN
         BEGIN
            State := Abort;       (* too many tries, abort *)
            ErrorMsg ('Send data - Too many');
         END
      ELSE
         BEGIN
            NumTry := NumTry+1;
            GetData(newstate);
            SendPacket;
            IF ReceiveACK THEN
               BEGIN
                  State := newstate;
                  NumTry := 0;
                  n := (n+1) MOD 64;
               END
         END;
   END;
 
PROCEDURE SendEOF;    (* send EOF  packet *)
   BEGIN
      Verbose ('Sending EOF');
      IF (NumTry > MaxTry) THEN
         BEGIN
            State := Abort;       (* too many tries, abort *)
            ErrorMsg('Send EOF - Too Many');
         END
      ELSE
         BEGIN
            NumTry := NumTry+1;
            IF (NumTry = 1) THEN
               BEGIN
                  WITH Buf[ThisPacket] DO
                     BEGIN
                        ptype := TYPEZ;
                        seq := n;
                        count := 0;
                     END
               END;
            SendPacket;
            IF ReceiveACK THEN
               BEGIN
                  State := FileHeader;
                  NumTry := 0;
                  n := (n+1) MOD 64;
               END
         END;
   END;
 
PROCEDURE SendBreak; (* send break packet *)
   BEGIN
      Verbose ('Sending break');
      IF (NumTry > MaxTry) THEN
         BEGIN
            State := Abort;       (* too many tries, abort *)
            ErrorMsg('Send break -Too Many');
         END
      ELSE
         BEGIN
            NumTry := NumTry+1;
            (* make up packet  *)
            IF (NumTry = 1) THEN
               BEGIN
                  WITH Buf[ThisPacket] DO
                     BEGIN
                        ptype := TYPEB;
                        seq := n;
                        count := 0;
                     END
               END;
            SendPacket; (* send this packet *)
            IF ReceiveACK THEN
               BEGIN
                  State := Complete;
               END
         END;
   END;
 
PROCEDURE SendInit;  (* send init packet *)
   BEGIN
      Verbose ('Sending Init');
      IF (NumTry > MaxTry) THEN
         BEGIN
            State := Abort;      (* too many tries, abort *)
            ErrorMsg('Cannot Initialize');
         END
      ELSE
         BEGIN
            NumTry := NumTry+1;
            IF (NumTry = 1) THEN
               BEGIN
                  WITH Buf[ThisPacket] DO
                     BEGIN
                        EnCodeParm(data);
                        count := NUMPARAM;
                        seq := n;
                        ptype := TYPES;
                     END
               END;
 
            SendPacket; (* send this packet *)
            IF ReceiveACK THEN
               BEGIN
                  WITH Buf[CurrentPacket] DO
                     BEGIN
                        SizeSend := UnChar(data[1]);
                        TheirTimeOut := UnChar(data[2]);
                        NumPad := UnChar(data[3]);
                        PadChar := Ctl(data[4]);
                        SendEOL := CR;  (* default to CR *)
                        IF (ilength(data) >= 5) THEN
                           IF (data[5] <> 0) THEN  SendEOL := UnChar(data[5]);
                        SendQuote := SHARP;  (* default # *)
                        IF (ilength(data) >= 6) THEN
                           IF (data[6] <> 0) THEN  SendQuote := data[6];
                     END;
 
                  State := FileHeader;
                  NumTry := 0;
                  n := (n+1) MOD 64;
               END;
         END;
   END;

 
PROCEDURE SendSwitch;
   (* Send-switch is the state table switcher for sending files.
    * It loops until either it is finished or a fault is encountered.
    * Routines called by sendswitch are responsible for changing the state.
    *)
 
   HANDLER GotErrorPacket(VAR msg: istring) ;
      (* We got an error packet when trying to receive another packet. *)
      (* (possibly an ACK). Write the packet data and exit SEND command *)
      BEGIN
         Inverse( TRUE ) ;
         Writeln ;
         Writeln('?SEND received an error packet from the other Host') ;
         putstr(msg, STDOUT) ;
         Writeln ;
         Inverse( FALSE ) ;
         SClose( DiskFile ) ; (* close the disk file if its open *)
         State := Abort ;
         EXIT( SendSwitch )
      END ;


   BEGIN
      LoadCurs ; (* Load the progress cursors *)
      State := Init;              (* send initiate is the start state *)
      NumTry := 0;                (* say no tries yet *)
      IF (Delay > 0) THEN Sleep(Delay);
      REPEAT
         CASE State OF
            FileData:     SendData;         (* data-send state *)
            FileHeader:   SendFile;         (* send file name *)
            EOFile:       SendEOF;          (* send end-of-file *)
            Init:         SendInit;         (* send initialize *)
            Break:        SendBreak;        (* send break *)
            Complete:     (* nothing *);
            Abort:        (* nothing *);
            END (* case *);
      UNTIL ( (State = Abort) OR (State=Complete) );

      QuitProgress ;  (* Remove progress cursors *)

   END.

