{ BINXFER.INC

  (c) 1986  Jeffry B. Jacobsen

  This implements the YAPP(tm) binary transfer protocol (or at least
  a subset of the full protocol - this version does not include the
  server commands for automated transfer.)

  This is a modified version of the actual code used in YAPP for the
  IBM PC and compatibles.  Some lines have been deleted that handled
  functions such as displaying the status of the transfer, and checking
  for an abort from the keyboard.

}


type

  states = (S,S1,SH,SD,SE,ST,R,RH,RD,Abort,CW,C,Start);
  paktype = (UK,RR,RF,SI,HD,DT,EF,ET,NR,CN,CA,RI,TX,UU,TM,AF,AT);
  std    = array[states] of string[11];

const stdesc: std = ('SendInit  ',
                     'SendInit  ',
                     'SendHeader',
                     'SendData  ',
                     'SendEof   ',
                     'SendEOT   ',
                     'RcvWait   ',
                     'RcvHeader ',
                     'RcvData   ',
                     'SndABORT  ',
                     'WaitAbtAck',
                     'RcdABORT  ',
                     'Start     ');

var

  Sendinit_Count : integer;
  xferhdr   : line;
  xfercnt   : real;
  xferok    : boolean;

  state     : states;
  ptype     : paktype;

  pkbuff    : array [1..256] of char;
  pklen     : integer;
  pkfile    : file of byte;
  pkfname   : string[30];
  txtbuff   : line;


const

  NUL       = #0;
  SOH       = #1;
  STX       = #2;
  ETX       = #3;
  EOT       = #4;
  ENQ       = #5;
  ACK       = #6;
  DLE       = #16;
  NAK       = #21;
  CAN       = #24;




function waitready: boolean;
{wait 20 seconds or 120 seconds for a character}

begin
  waitready := false;
  if (state = S) or (state = S1) then
    set_timer(20)             { 20 seconds to timeout}
  else
    set_timer(120);           {120 seconds to timeout}
  repeat
    if timeout then begin     {timeout checks timer value which is}
      ptype := TM;            {decremented towards 0 every second }
      waitready := true;
      exit;
      end;
  until inready;              {inready checks for character ready at TNC}
end;


procedure getpkstr;

  var
    i : integer;
    ch : char;

  begin
    if waitready then exit;
    ch := recvchar;           {recvchar returns character from TNC}
    pklen := ord(ch);
    if (ptype = DT) and (pklen = 0) then pklen := 256;
    if (pklen = 0) then exit;
    for i := 1 to pklen do
      begin
      if waitready then exit;
      ch := recvchar;
      pkbuff[i] := ch;
      end;
   end;


procedure Getpack;

  var
    ch : char;

  begin
    ptype := UK;
    if waitready then exit;
    ch := recvchar;
    case ch of
      ACK:  begin
            if waitready then exit;
            ch := recvchar;
            case ord(ch) of
              1: ptype := RR;
              2: ptype := RF;
              3: ptype := AF;
              4: ptype := AT;
              5: ptype := CA;
              else;
              end;
            end;
      ENQ:  begin
            if waitready then exit;
            ch := recvchar;
            case ord(ch) of
              1: ptype := SI;
              2: ptype := RI;
              else ptype := UU; {unimplemented command}
              end;
            end;
      SOH:  begin
            ptype := HD;
            getpkstr;
            end;
      STX:  begin
            ptype := DT;
            getpkstr;
            end;
      ETX:  begin
            if waitready then exit;
            ch := recvchar;
            if (ord(ch) = 1) then ptype := EF;
            end;
      EOT:  begin
            if waitready then exit;
            ch := recvchar;
            if (ord(ch) = 1) then ptype := ET;
            end;
      NAK:  begin
            ptype := NR;
            getpkstr;
            end;
      CAN:  begin
            ptype := CN;
            getpkstr;
            end;
      DLE:  begin
            ptype := TX;
            getpkstr;
            end;
    else;
    end; {case}
  end;


procedure Sendinit;

  begin
    Sendinit_Count := 0;
    xmitstr(ENQ + #01);          {send string to TNC}
    getpack;
    case ptype of
      TM : state := S1;
      RI : state := S;
      RR : state := SH;
      RF : state := SD;
      CN : state := C;
      NR : state := Start;
      TX : disppacket;
      else begin
           state := Abort;
           showmsg(13);          {error message display}
           end;
      end;
    end;



procedure Sendinit_retry;

  begin
    Sendinit_Count := Sendinit_Count + 1;
    if (Sendinit_Count > 6) then begin
      state := Abort;
      showmsg(12);
      exit;
      end;
    xmitstr(ENQ + #01);
    getpack;
    case ptype of
      TM : state := S1;
      RI : state := S;
      RR : state := SH;
      RF : state := SD;
      CN : state := C;
      NR : state := Start;
      TX : disppacket;
      else begin
           state := Abort;
           showmsg(13);
           end;
      end;
    end;



procedure Sendhdr;

  var
    stlen : byte;

  begin
    temp := pkfname + NUL + filesize + NUL;
    xferhdr := temp;
    showheader;                          {display}
    stlen := length(temp);
    xmitstr(SOH + chr(stlen) + temp);
    getpack;
    case ptype of
      RF : state := SD;
      CN : state := C;
      NR : state := Start;
      TX : disppacket;
      else begin
           state := Abort;
           if (ptype = TM) then showmsg(12)
             else showmsg(13);
           end;
      end;
    end;



procedure Senddata;

  var
    i,cnt : integer;
    bte : byte;
    temp : array [1..256] of char;
    ch: char;
    scancode: integer;

  begin
    if inready then begin     {we shouldnt be getting a packet   }
      getpack;                {unless they sent a Cancel or Text }
      if (ptype = CN) then begin
        state := C;
        exit;
        end
      else if (ptype = TX) then 
        disppacket
      else begin
        writeln('Unexpected packet type during Send!');
        state := Abort;
        exit;
        end;
      end;
    cnt := 0;
    while (not eof(pkfile)) and (cnt < 256) do
      begin
      cnt := cnt + 1;
      read(pkfile,bte);
      temp[cnt] := chr(bte);
      end;
    if cnt <> 0 then
      begin
      if cnt = 256 then bte := 0 else bte := cnt;
      xmitstr(STX + chr(bte));
      for i := 1 to cnt do
        xmitchar(temp[i]);
      end;
    if cnt < 256 then state := SE;
    xfercnt := xfercnt + cnt;
  end;


procedure SendEOF;

  begin
    xmitstr(ETX + #01);
    getpack;
    case ptype of
      AF : state := ST;
      TX : disppacket;
      else begin
           state := Abort;
           if (ptype = TM) then showmsg(12)
             else showmsg(13);
           end;
      end;
    end;


procedure SendEOT;

  begin
    xmitstr(EOT + #01);
    getpack;
    case ptype of
      AT : state := Start;  {Ack ok}
      TX : disppacket;
      else state := Start;  {They sent AF - so dont worry about it}
    end;
  end;


procedure Receive;

  begin
    getpack;
    case ptype of
      SI : begin
           showmsg(1);
           xmitstr(ACK + #01);
           state := RH;
           end;
      CN : state := C;
      TX : disppacket;
      else begin
           state := Abort;
           if (ptype = TM) then showmsg(12)
             else showmsg(13);
           end;
      end;
end;


procedure RcvHdr;

var
  i : integer;
  temp : line;

  begin
    temp := '';
    getpack;
    case ptype of
      HD : begin
           for i := 1 to pklen
             do temp := temp + pkbuff[i];
           xferhdr := temp;
           showheader;
           xmitstr(ACK + #02);
           state := RD;
           end;
      SI : state := RH;
      CN : state := C;
      ET : begin
           xmitstr(ACK + #04);
           state := Start;
           end;
      TX : disppacket;
      else begin
           state := Abort;
           if (ptype = TM) then showmsg(12)
             else showmsg(13);
           end;
      end;
    end;


procedure RcvData;

var
  i : integer;
  bte : byte;

  begin
    getpack;
    case ptype of
      DT : begin
           for i := 1 to pklen do
             begin
             bte := ord(pkbuff[i]);
             write(pkfile,bte);
             end;
           xfercnt := xfercnt + pklen;
           showbytes;
           state := RD;
           end;
      EF : begin
           close(pkfile);
           xferok := TRUE;
           showmsg(8);
           xmitstr(ACK + #03);
           state := RH;
           end;
      CN : state := C;
      TX : disppacket;
      else begin
           state := Abort;
           if (ptype = TM) then showmsg(12)
             else showmsg(13);
           end;
      end;
    end;



procedure Cancel;

  begin
    xmitstr(CAN + #00);
    state := CW;
  end;


procedure CanWait;

  begin
    escmsg(10);
    getpack;
    case ptype of
      CA : state := Start;
      CN : xmitstr(ACK + #05);
      TM : state := Start;
      UK : state := Start;
      TX : disppacket;
      else;
    end;
  end;



procedure CanRecd;

var
  i : integer;
  bte : byte;

  begin
    showmsg(11);
    xmitstr(ACK + #05);
    delay(3000);   {see if this helps the stupid TNC-2s problem!}
    state := Start;
  end;



procedure xfer;

begin
  xferhdr := '';
  xfercnt := 0;
  xmitline('t');   {put TNC into transparent mode}
  delay(50);
  txtbuff := '';
  repeat
    showstate;     {display state}
    case state of
      S: Sendinit;
     S1: Sendinit_retry;
     SH: Sendhdr;
     SD: Senddata;
     SE: SendEOF;
     ST: SendEOT;
      R: Receive;
     RH: Rcvhdr;
     RD: Rcvdata;
  Abort: Cancel;
     CW: CanWait;
      C: CanRecd;
    else;
    end; {case}
  until (state = Start);

  write(#07);     {bell}
  delay(1000);    {give TNC some time}
  cmdmode;        {get into command mode}
  flush;
  xmitline('conv');  {back to converse mode}
end;


procedure upload;

  begin
    pkfname := getfilname('Upload Filename: ');
    Assign(pkfile,pkfname);
    reset(pkfile);
    state := S;
    xfer;
    close(pkfile);
  end;


procedure download;

  begin
    pkfname := getfilname('Enter Filename: ');
    assign(pkfile,pkfname);
    rewrite(pkfile);
    state := R;
    xferok := FALSE;
    xfer;
    if not xferok then begin
      close(pkfile);
      erase(pkfile);
      end;
  end;

