|x|*|f6|*|f5|*|f4|*|f3|*|f2|*|f1|{bootstrap the function keys}|.
jff/Change log:/|nsm$log|nqan|{locate & mark the Change log}|.
cfucp1.1upd[begin,end]|n|f6ucp1.1upd|n|{get specified part}|.
bsmbegin|n2fsbsmend|nqa,|{mark beginning and ending lines of this part}|.
jmend|nf/>>>>/ d|g}|!|*c|f1|f4ramdisk:|f1|n|f5|{save next part to ramdisk:}|.
|f3|f3|f3|{main extraction sequence}|.
|xsmend|njfk/|d|e|f2|{extraction initialization, replaced by filename}|.
{>>>> DIR.FIXES.TEXT}
unit dir_fixes;

{ Change log:
25 Jul 90 (RTC): added some error handling code
18 Jul 90 (RTC): Created to fix limitations of dir.info under SFS
}
   
interface
  
  type
    dTimeRec = packed record
                 min : 0..59;
                 hour : 0..24
               end {dTimeRec};
  
  procedure get_lastblk(dunit : integer; var filename : string; 
                        var bytes : integer);
  
  procedure put_lastblk(dunit : integer; var filename : string; 
                        bytes : integer);
  
  procedure get_filetime(dunit : integer; var filename : string; 
                         var the_time : dTimeRec);
  
  procedure put_filetime(dunit : integer; var filename : string; 
                         the_time : dTimeRec);

implementation

  uses
    {$U syslibr:kernel.code} kernel (directory,dirrange,dirblk,maxdir);
  
  function get_file(dunit : integer; var filename : string;
                    var dir : directory) : dirrange;
    
    var i,j : dirrange;
    
    begin {get_file}
      unitread(dunit,dir,sizeof(directory),dirblk);
      j := 0 {invalid entry number, in case we don't find it};
      for i := 1 to maxdir do
        if filename = dir[i].dtid
          then j := i;
      get_file := j;
      if j = 0 then
        begin
          writeln;
          writeln(chr(7),'ERROR! File "',filename,
                  '" missing from directory of unit #',dunit);
        end
    end {get_file};
  
  procedure put_file(dunit : integer; var dir : directory);
    
    begin {put_file}
      unitwrite(dunit,dir,sizeof(directory),dirblk);
    end {put_file};
  
  procedure get_lastblk{dunit : integer; var filename : string; 
                        var bytes : integer};
    
    var
      disk_dir : directory;
    
    begin {get_lastblk}
      bytes := disk_dir[get_file(dunit,filename,disk_dir)].dlastbyte
    end {get_lastblk};
  
  procedure put_lastblk{dunit : integer; var filename : string; 
                        bytes : integer};
    
    var
      item : dirrange;
      disk_dir : directory;
    
    begin {put_lastblk}
      item := get_file(dunit,filename,disk_dir);
      if item <> 0 then
        begin
          disk_dir[item].dlastbyte := bytes;
          put_file(dunit,disk_dir)
        end
    end {put_lastblk};
  
  procedure get_filetime{dunit : integer; var filename : string; 
                         var the_time : dTimeRec};
    
    var
      disk_dir : directory;
    
    begin {get_filetime}
      with the_time,disk_dir[get_file(dunit,filename,disk_dir)] do
        begin
          min := dminute; hour := (dhour + 24) mod 25 {pred(dhour)}
        end;
    end {get_filetime};
  
  procedure put_filetime{dunit : integer; var filename : string; 
                         the_time : dTimeRec};
    
    var
      item : dirrange;
      disk_dir : directory;
    
    begin {put_filetime}
      item := get_file(dunit,filename,disk_dir);
      if item <> 0 then
        with the_time,disk_dir[item] do
          begin
            dminute := min; dhour := succ(hour) mod 25;
            put_file(dunit,disk_dir)
          end
    end {put_filetime};

end. { dir.fixes }
{>>>> SENDER.TEXT}
{$D AFS-}  { indicates to compile to run without Adv. File Sys.}

unit sender;

interface

{Change log:
25 Jul 90, V1.1: Fixed invalid time attribute bug   RTC
18 Jul 90, V1.1: Fixed SFS limitations   RTC
13 May 89, V1.1: Misc. cleanups to debug messages   RTC
26 Apr 89, V1.1: minor cleanups   RTC
16 Apr 89, V1.1: Fixed "garbage in buffer" bug         RTC
13 Apr 89, V1.1: Added Version message          RTC
14 Aug 88: Fixed timeout state bug       RTC
07 Aug 88: Added conditional compilation for AFS/SFS difference    RTC
31 Jul 88: Added Attributes Packets & cancel xfr request from receiver  RTC
10 Jul 88: Converted to use screenops unit     RTC
10 Jul 88: Fixed cleareol problem on filenames      RTC
02 Jul 88: Fixed sinit 8th-bit prefix negotiation bug     RTC
30 Jun 88: Added Binary and multiple file transfers    RTC

}

   procedure sendsw(var send_ok: boolean);
   
   procedure sen_version;


implementation

uses
   screenops,   {RTC, 10 Jul 88}
   {$U kermglob.code} kermglob,
   {$U kermutil.code} kermutil,
   {$U kermpack.code} kermpack,
   {$B AFS+} 
   {$U syslibr:attribute.code} attributes, 
   {$E AFS+} {$B AFS-}
   {$U dir.fixes.code} dir_fixes,
   {$E AFS-}
   {$U syslibr:wild.code} wild,
   {$U syslibr:dir.info.code} dirinfo;

const
  my_version = '   Sender Unit V1.1, 25 Jul 90';


procedure sendsw{(var send_ok: boolean)};

var
  do_attr, still_sending, discard, next_is_empty : boolean;
  files_to_send : D_listp;
  io_status: integer;
  heap: ^integer;
  {$B AFS-}
  this_file : D_listp;
  {$E AFS-}

procedure openfile;

(* resets file of appropriate type *)
  
  var
    dummy : boolean;

  begin
    if debug then
        debugwrite(concat('Opening ',xfilename));
    (*$I-*) (* turn off compiler i/o checking temporarily *)
    if f_is_binary
      then
        begin
          reset(b_file,xfilename);
          if io_result = 0 then
            {$B AFS+}
            dummy := get_attribute(b_file,FA_lastvalidbyte,last_blksize);
            {$E AFS+} {$B AFS-}
            get_lastblk(files_to_send^.dunit,xfilename,last_blksize);
            {$E AFS-}
          bufend := 0                   {mark the buffer as empty!}
        end
      else reset(t_file,xfilename);
    (*$I+*) (* turn compiler i/o checking back on *)
    io_status := io_result;
    {$B AFS-}
    this_file := files_to_send;
    {$E AFS-}
  end; (* openfile *)

function sinit: char;

(* send init packet & receive other side's *)

var num, len, i: integer;  (* packet number and length *)
    ch: char;

  begin
    if debug then
        debugwrite('sinit');

    if numtry > maxtry then
      begin
        sinit := 'a';
        exit(sinit)
      end;

    num_try := num_try + 1;
    spar(packet);

    clear_buf(inport);

    refresh_screen(numtry,n);

    spack('S',n mod 64,10,packet);

    ch := rpack(len,num,recpkt);

    if (ch = 'N') then
      begin
        sinit := 's';
        exit(sinit)
      end (* if 'N' *)
    else if (ch = 'Y') then
      begin
        if ((n mod 64) <> num) then       (* not the right ack *)
          begin
            sinit := currstate;
            exit(sinit)
          end;
        rpar(recpkt,len);
        if (xeol = chr(0)) then   (* if they didn't spec eol *)
            xeol := chr(my_eol);    (* use mine *)
        if (quote = chr(0)) then (* if they didn't spec quote *)
            quote := my_quote;     (* use mine *)
        ctl_set := [chr(0)..chr(31),chr(del),quote];
        if en_qbin then ctl_set := ctl_set + [qbin];
        numtry := 0;
        n := n + 1;              (* increase packet number *)
        sinit := 'f';
        exit(sinit)
      end (* else if 'Y' *)
    else if (ch = 'E') then
      begin
        error(recpkt,len);
        sinit := 'a'
      end (* if 'E' *)
    else if (ch = chr(0)) then
        sinit := currstate
    else if (ch <> 'N') then
        sinit := 'a'
  end; (* sinit *)

function sattr: char;

(* send attributes packet *)

var num, len, pkt_len: integer;
    ch: char;
    got_attr : boolean;
    {$B AFS+} 
    file_date : FA_chron;
    {$E AFS+} {$B AFS-} 
    file_time : dTimeRec;
    {$E AFS-}
    packet : packettype;

  begin
    if debug then
        debugwrite('sattr');

    if numtry > maxtry then
      begin
        sattr := 'a';
        exit(sattr)
      end;

    num_try := num_try + 1;

    refresh_screen(numtry,n);
    
    {$B AFS+}
    if f_is_binary
      then got_attr := get_attribute(b_file,FA_revision_date,file_date)
      else got_attr := get_attribute(t_file,FA_revision_date,file_date);
    with file_date,date,time do
    {$E AFS+} {$B AFS-}
    get_filetime(this_file^.dunit,xfilename,file_time);
    with this_file^.D_date,file_time do
    {$E AFS-}
      begin
        packet[0] := '#';                   { creation date attribute }
        
        packet[2] := chr(year div 10 + ord('0'));
        packet[3] := chr(year mod 10 + ord('0'));
        packet[4] := chr(month div 10 + ord('0'));
        packet[5] := chr(month mod 10 + ord('0'));
        packet[6] := chr(day div 10 + ord('0'));
        packet[7] := chr(day mod 10 + ord('0'));
        pkt_len := 8;
        if hour <> 24
          then {valid time}
            begin
              packet[8] := ' ';
              packet[9] := chr(hour div 10 + ord('0'));
              packet[10] := chr(hour mod 10 + ord('0'));
              packet[11] := ':';
              packet[12] := chr(min div 10 + ord('0'));
              packet[13] := chr(min mod 10 + ord('0'));
              packet[1] := tochar(chr(12));       { length }
              pkt_len := pkt_len + 6
            end
          else {invalid time}
            begin
              packet[1] := tochar(chr(6));       { length }
            end
      end;

    spack('A',n mod 64,pkt_len,packet);

    ch := rpack(len,num,recpkt);

    if (ch = 'N') then
      begin
        sattr := 'd';
        exit(sattr)
      end (* if 'N' *)
    else if (ch = 'Y') then
      begin
        if ((n mod 64) <> num) then       (* not the right ack *)
          begin
            sattr := currstate;
            exit(sattr)
          end;
        numtry := 0;
        n := n + 1;              (* increase packet number *)
        do_attr := false;
        discard := (len > 0) and (recpkt[0] = 'N');
        if discard
          then sattr := 'z'
          else sattr := 'd';
        exit(sattr)
      end (* else if 'Y' *)
    else if (ch = 'E') then
      begin
        error(recpkt,len);
        sattr := 'a'
      end (* if 'E' *)
    else if (ch = chr(0)) then
        sattr := currstate
    else if (ch <> 'N') then
        sattr := 'a'
  end; (* sattr *)

function sdata: char;

(* send file data *)

var num, len: integer;
    ch: char;
    packarray: array[boolean] of packettype;
    sizearray: array[boolean] of integer;
    current: boolean;
    b: boolean;

function other(b: boolean): boolean;

(* complements a boolean which is used as array index *)

  begin
    if b then
        other := false
    else
        other := true
  end; (* other *)

  begin
    discard := false;
    current := true;
    packarray[current] := packet;
    sizearray[current] := size;
    next_is_empty := true;
    while (currstate = 'd') do
      begin
        if (numtry > maxtry) then             (* if too many tries, give up *)
            currstate := 'a';

        b := other(current);
        numtry := numtry + 1;

                                          (* send a data packet *)
        spack('D',n mod 64,sizearray[current],packarray[current]);

        refresh_screen(numtry,n);
        
        if next_is_empty then             (* set up next packet *)
          begin
            sizearray[b] := bufill(packarray[b]);
            next_is_empty := false
          end;

        ch := rpack(len,num,recpkt);      (* receive a packet *)
        if ch = 'N' then                  (* NAK, so just stay in this state *)
            if ((n+1) mod 64 <> num) then (* unless NAK for next packet, which 
*)
                sdata := currstate
            else                          (* is just like ACK for this packet *
)
              begin
                if num > 0 then
                    num := (num - 1)      (* in which case, decrement num *)
                else
                    num := 63;
                ch := 'Y';                (* and indicate an ACK *)
              end; (* else *)

        if (ch = 'Y') then
           begin
             if ((n mod 64) <> num) then (* if wrong ACK *)
               (* stay in same state *)
             else
               begin
                 numtry := 0;
                 n := n + 1;
                 current := b;
                 next_is_empty := true;
                 discard := sizearray[current] = at_badblk;
                 if read_ch(keyport, ch) then {check for user canceling send}
                   begin
                     if ord(ch) in [can_cur,can_all]
                       then discard := true;
                     if ord(ch) = can_all
                       then files_to_send := nil
                   end;
                 if len = 1 then {check for receiver canceling send}
                   begin
                     if recpkt[0] in ['X','Z']
                       then discard := true;
                     if recpkt[0] = 'Z'
                       then files_to_send := nil
                   end;
                 if (sizearray[current] = at_eof) or discard then
                     currstate := 'z'            (* set state to eof *)
                 else
                     currstate := 'd'            (* else stay in data state *)
               end {else}
           end (* if *)
          else if (ch = 'E') then
            begin
              error(recpkt,len);
              currstate := 'a'
            end (* if 'E' *)
          else if (ch = chr(0)) then      (* receive failure, so stay in d *)
          else if (ch <> 'N') then
            currstate := 'a'                  (* on anything else goto abort st
ate *)
      end; (* while *)
    size := sizearray[current];
    packet := packarray[current];
    sdata := currstate
  end; (* sdata *)

function sfile: char;

(* send file header *)

var num, len, i: integer;
    ch: char;
    fn: packettype;
    oldfn: string255;

procedure legalize(var fn: string255);

(* make sure we send only 1 '.' in filename *)

var count, i, j, l: integer;

  begin
    if not lit_names then
      begin
        count := 0;
        l := length(fn);
        for i := 1 to l do                                  (* count '.'s in fn
 *)
            if fn[i] = '.' then
                count := count + 1;
        for i := 1 to count-1 do                            (* remove all but 1
 *)
          begin
            j := 1;
            while (j < l) and (fn[j] <> '.') do
                j := j + 1;                                 (* by finding it *)
            fn := concat(copy(fn,1,j-1),copy(fn,j+1,l-j));  (* and copying arou
nd it *)
            l := l - 1
          end (* for i *)
      end;
    i := pos(':',fn);
    if i <> 0 then
      fn := copy(fn,i+1,length(fn)-i)         {remove Vol. name}
  end; (* legalize *)

  begin
    if debug then
        debugwrite('sfile');

    if (numtry > maxtry) then          (* if too many tries, give up *)
      begin
        sfile := 'a';
        exit(sfile)
      end;
    numtry := numtry + 1;

    oldfn := xfilename;
    legalize(xfilename);                (* make filename acceptable to remote *
)
    len := length(xfilename);

    moveleft(xfilename[1],fn[0],len);   (* move filename into a packettype *)

    SC_erase_to_EOL(filepos,fileline);
    write(oldfn,' ==> ',xfilename);

    refresh_screen(numtry,n);

    spack('F',n mod 64,len,fn);               (* send file header packet *)

    if next_is_empty then
      begin
        size := bufill(packet);            (* get first data from file *)
        next_is_empty := false
      end;                             (* while waiting for response *)

    ch := rpack(len,num,recpkt);
    if ch = 'N' then                   (* NAK, so just stay in this state *)
        if ((n+1) mod 64 <> num) then  (* unless NAK for next packet, which *)
          begin
            sfile := 'f';
            exit(sfile)                (* is just like ACK for this packet *)
          end
        else
          begin
            if (num > 0) then
                num := (num - 1)       (* in which case, decrement num *)
            else
                num := 63;
            ch := 'Y';                 (* and indicate an ACK *)
          end; (* else *)

    if (ch = 'Y') then
      begin
        if ((n mod 64) <> num) then  (* if wrong ACK, stay in F state *)
          begin
            sfile := 'f';
            exit(sfile)
          end;
        numtry := 0;
        n := n + 1;
        do_attr := en_attr;
        sfile := 'd';
      end (* if *)
    else if (ch = 'E') then
      begin
        error(recpkt,len);
        sfile := 'a'
      end (* if 'E' *)
    else if (ch = chr(0)) then  {stay in f state}
        sfile := 'f'
    else if (ch <> 'N') then (* don't recognize it *)
        sfile := 'a'
  end; (* sfile *)

function seof: char;

(* send end of file *)

var num, len: integer;
    ch: char;

  begin
    if debug then
        debugwrite('seof');

    if (numtry > maxtry) then          (* if too many tries, give up *)
      begin
        seof := 'a';
        exit(seof)
      end;
    numtry := numtry + 1;

    refresh_screen(numtry,n);

    packet[0] := 'D';           {set up in case of discard}
    
    spack('Z',(n mod 64),ord(discard),packet);    (* send end of file packet *)

    if debug then
        debugwrite('seof1');

    ch := rpack(len,num,recpkt);
    if ch = 'N' then                   (* NAK, so just stay in this state *)
        if ((n+1) mod 64 <> num) then  (* unless NAK for next packet, which *)
          begin
            seof := 'z';
            exit(seof)                 (* is just like ACK for this packet *)
          end
        else
          begin
            if num > 0 then
                num := (num - 1)       (* in which case, decrement num *)
            else
                num := 63;
            ch := 'Y';                 (* and indicate an ACK *)
          end; (* else *)

    if (ch = 'Y') then
      begin
        if debug then
            debugwrite('seof2');
        if ((n mod 64) <> num) then     (* if wrong ACK, stay in Z state *)
          begin
            seof := 'z';
            exit(seof)
          end;
        numtry := 0;
        n := n + 1;
        if debug then
            debugwrite(concat('Closing ',xfilename));
        if f_is_binary
          then close(b_file)
          else close(t_file);
        while files_to_send <> nil do with files_to_send^ do
          begin
            xfilename := concat(D_volume,':',D_title);
            seof := 'f';
            next_is_empty := true;
            
            openfile;
            files_to_send := D_next_entry;
            if io_status <> 0
              then io_error(io_status)
              else exit(seof)
          end {while};
        seof := 'b'
      end (* if *)
    else if (ch = 'E') then
      begin
        error(recpkt,len);
        seof := 'a'
      end (* if 'E' *)
    else if (ch = chr(0)) then         (* receive failed, so stay in z state *)
        seof := 'z'
    else if (ch <> 'N') then           (* other error, just abort *)
        seof := 'a'
  end; (* seof *)

function sbreak: char;

var num, len: integer;
    ch: char;

(* send break (end of transmission) *)

  begin
    if debug then
        debugwrite('sbreak');

    if (numtry > maxtry) then          (* if too many tries, give up *)
      begin
        sbreak := 'a';
        exit(sbreak)
      end;
    numtry := numtry + 1;

    refresh_screen(numtry,n);

    spack('B',(n mod 64),0,packet);    (* send Break Transfer packet *)

    ch := rpack(len,num,recpkt);
    if ch = 'N' then                   (* NAK, so just stay in this state *)
        if ((n+1) mod 64 <> num) then  (* unless NAK for next packet, which *)
          begin
            sbreak := 'b';
            exit(sbreak)               (* is just like ACK for this packet *)
          end
        else
          begin
            if num > 0 then
                num := (num - 1)       (* in which case, decrement num *)
            else
                num := 63;
            ch := 'Y';                 (* and indicate an ACK *)
          end; (* else *)

    if (ch = 'Y') then
      begin
        if ((n mod 64) <> num) then    (* if wrong ACK, stay in B state *)
          begin
            sbreak := 'b';
            exit(sbreak)
          end;
        numtry := 0;
        n := n + 1;
        sbreak := 'c'                  (* else, switch state to complete *)
      end (* if *)
    else if (ch = 'E') then
      begin
        error(recpkt,len);
        sbreak := 'a'
      end (* if 'E' *)
    else if (ch = chr(0)) then         (* receive failed, so stay in b state *)
        sbreak := 'b'
    else if (ch <> 'N') then           (* other error, just abort *)
        sbreak := 'a'
  end; (* sbreak *)

(* state table switcher for sending *)

  begin (* sendsw *)
    mark(heap);
    send_ok := false;
    still_sending := 
        D_dirlist(xfilename,[D_code..D_svol],files_to_send,false) = D_okay;
    if files_to_send <> nil then with files_to_send^ do
      begin
        xfilename := concat(D_volume,':',D_title);
        next_is_empty := true;
        
        openfile;
        files_to_send := D_next_entry;
        if io_status <> 0 then
          begin
            io_error(io_status);
            still_sending := false
          end
      end;

    if still_sending then write_screen('Sending');
    currstate := 's';
    n := 0;       (* set packet # *)
    numtry := 0;
    flush_comm;         {flush any garbage in buffer}
    
    while still_sending do
        if currstate in ['d', 'f', 'z', 's', 'b', 'c', 'a'] then
          case currstate of
              'd': if do_attr
                     then currstate := sattr
                     else currstate := sdata;
              'f': currstate := sfile;
              'z': currstate := seof;
              's': currstate := sinit;
              'b': currstate := sbreak;
              'c': begin
                     send_ok := true;
                     still_sending := false
                   end; (* case c *)
              'a': still_sending := false
            end (* case *)
        else (* state not in legal states *)
          begin
            debugwrite('Unknown State');
            still_sending := false
          end (* else *);
    release(heap)
  end; (* sendsw *)

procedure sen_version;
  
  begin
    writeln(my_version)
  end {sen_version};

end. { sender }
{>>>> RECEIVER.TEXT}
{$D AFS-}       {indicates for compile to run without Adv. File Sys.}

unit receiver;

interface

{Change log:
18 Jul 90, V1.1: Fixed SFS limitations   RTC
18 May 89, V1.1: Added debugdate to reread file dates (fixed date bug[??]) RTC
13 May 89, V1.1: Misc. cleanup to debug messages   RTC
30 Apr 89, V1.1: Fixed receiver won't stop on maxtry bug   RTC
26 Apr 89, V1.1: minor cleanups   RTC
16 Apr 89, V1.1: Fixed "garbage in buffer" bug       RTC
16 Apr 89, V1.1: Fixed "short text filename" bug.   RTC
15 Apr 89, V1.1: Added GET protocol & debug logging of date set result    RTC
13 Apr 89, V1.1: Added version message          RTC
17 Aug 88: Fixed garbage after partial last block of bin. file    RTC
07 Aug 88: Added conditional compilation for AFS/SFS differences   RTC
31 Jul 88: Added Attribute Packets & user discard requests to sender   RTC
10 Jul 88: Converted to use screenops unit     RTC
10 Jul 88: Fixed cleareol problem on filenames     RTC
02 Jul 88: Added binary file transfer & discard protocol   RTC

}
  
  procedure recsw(var rec_ok: boolean; get_from_server : boolean);
  
  procedure rec_version;


implementation

uses
   screenops,   {RTC, 10 Jul 88}
   {$U kermglob.code} kermglob,
   {$U kermutil.code} kermutil,
   {$U kermpack.code} kermpack,
   {$B AFS+} 
   {$U syslibr:attribute.code} attributes; 
   {$E AFS+} {$B AFS-} 
   {$U dir.fixes.code} dir_fixes,
   {$U syslibr:wild.code} wild,
   {$U syslibr:dir.info.code} dirinfo;
   {$E AFS-}

const
  my_version = '   Receiver Unit V1.1, 18 Jul 90';

{$B AFS-}
procedure debugdate;
  
  var
    heap : ^integer;
    list : D_listp;
    rslt : D_result;
  
  begin {debugdate}
    mark(heap);
    rslt := D_dirlist(xfilename,[Dvol..Ddir],list,false);
    if rslt <> D_okay then debugwrite('Can''t Access File Date');
    if debug then with list^,D_date do
      begin
        debugwrite('');
        write(debf,'File ',D_volume,':',D_title,' Current Date = ',
              month,'/',day,'/',year)
      end;
    release(heap)
  end {debugdate};
{$E AFS-}

procedure recsw{(var rec_ok: boolean; get_from_server : boolean)};

var
  date_attr : record
                valid : boolean;
                value : {$B AFS+} FA_chron {$E AFS+}
                        {$B AFS-}
                        record
                          date : D_daterec;
                          time : D_timerec
                        end;
                        {$E AFS-}
              end;

function bufattr(buffer : packettype; len : integer) : integer;
  
  var
    sp_pos,i,j,buffered : integer;
    tempattr : string;
  
  begin {bufattr}
    packet[0] := 'Y'; buffered := 1;    {agree to accept file}
    i := 0; while i < len do
      begin
        if buffer[i] in ['#'] then      {acceptable attribute}
          begin
            tempattr := '';
            for j := 1 to ord(unchar(buffer[succ(i)])) do
              begin
                tempattr := concat(tempattr,' ');
                tempattr[length(tempattr)] := buffer[succ(i) + j]
              end;
            case buffer[i] of
              '#' : with date_attr,value,date,time do
                begin
                  sp_pos := pos(' ',tempattr);
                  if sp_pos = 0 then sp_pos := succ(length(tempattr));
                  year := (ord(tempattr[sp_pos-6]) - ord('0')) * 10
                        + (ord(tempattr[sp_pos-5]) - ord('0'));
                  month := (ord(tempattr[sp_pos-4]) - ord('0')) * 10
                         + (ord(tempattr[sp_pos-3]) - ord('0'));
                  day := (ord(tempattr[sp_pos-2]) - ord('0')) * 10
                       + (ord(tempattr[sp_pos-1]) - ord('0'));
                  if length(tempattr) > sp_pos then
                    begin
                      hour := (ord(tempattr[sp_pos+1]) - ord('0')) * 10
                            + (ord(tempattr[sp_pos+2]) - ord('0'));
                      min := (ord(tempattr[sp_pos+4]) - ord('0')) * 10
                            + (ord(tempattr[sp_pos+5]) - ord('0'))
                    end
                  else          {no time provided}
                    begin
                      hour := 24 {non-valid time}; min := 0
                    end;
                  valid := true
                end
            end {case}
          end
        else                            {reject attribute}
          begin
            packet[buffered] := buffer[i];
            buffered := succ(buffered)
          end;
        i := succ(succ(i) + ord(unchar(buffer[succ(i)])))
      end;
    bufattr := buffered
  end {bufattr};

function rdata: char;

(* receive file data *)

var dummy, num, len: integer;
    ch: char;
    {$B AFS+}
    did_attr : boolean;
    {$E AFS+} {$B AFS-}
    heap : ^integer;
    this_file : D_listp;
    {$E AFS-}
    i: integer;

  begin

    repeat
        debugwrite('rdata');
        
        if numtry > maxtry then
          begin
            currstate := 'a';
            exit(rdata)
          end;
        num_try := num_try + 1;

        ch := rpack(len,num,recpkt);   (* receive a packet *)

        refresh_screen(numtry,n);

        if (ch = 'D') then             (* got data packet *)
          begin
            if (num <> (n mod 64)) then (* wrong packet *)
              begin
                if (oldtry > maxtry) then
                  begin
                    rdata := 'a';      (* too many tries, abort *)
                    exit(rdata)
                  end; (* if *)

                if (num = (pred(n) mod 64)) then (* previous packet again *)
                  begin                (* so re-ACK it *)
                    spack('Y',num,0,packet);
                    numtry := 0;       (* reset try counter *)
                                       (* stay in same state *)
                  end (* if *)
                else                   (* wrong number *)
                    currstate := 'a'       (* so abort *)
              end (* if *)
            else                       (* right packet *)
              begin
                bufemp(recpkt,len);  (* write data to file *)
                if read_ch(keyport, ch) then {check if user wants to can}
                  packet[0] := ctl(ch);
                spack('Y',(n mod 64),ord(ord(ch) in [can_cur,can_all]),
                      packet); (* ACK packet *)
                oldtry := numtry;      (* reset try counters *)
                numtry := 0;
                n := n + 1             (* bump packet number *)
                                       (* stay in data receive state *)
              end (* else *)
          end (* if 'D' *)
        else if ch = 'A' then           { Attributes }
          begin
            if (num <> (n mod 64)) then (* wrong packet *)
              begin
                if (oldtry > maxtry) then
                  begin
                    rdata := 'a';      (* too many tries, abort *)
                    exit(rdata)
                  end; (* if *)

                if (num = (pred(n) mod 64)) then (* previous packet again *)
                  begin                (* so re-ACK it *)
                    spack('Y',num,0,packet);
                    numtry := 0;       (* reset try counter *)
                                       (* stay in same state *)
                  end (* if *)
                else                   (* wrong number *)
                    currstate := 'a'       (* so abort *)
              end (* if *)
            else                       (* right packet *)
              begin
                spack('Y',(n mod 64),bufattr(recpkt,len),packet); (* ACK packet
 *)
                oldtry := numtry;      (* reset try counters *)
                numtry := 0;
                n := n + 1             (* bump packet number *)
                                       (* stay in data receive state *)
              end (* else *)
          end {if 'A'}
        else if (ch = 'F') then        (* file header *)
          begin
            if (oldtry > maxtry) then
              begin
                rdata := 'a';          (* too many tries, abort *)
                exit(rdata)
              end; (* if *)

            if (num = (pred(n) mod 64)) then (* previous packet again *)
              begin                    (* so re-ACK it *)
                spack('Y',num,0,packet);
                numtry := 0;           (* reset try counter *)
                                               (* stay in same state *)
              end (* if *)
            else
                currstate := 'a'           (* not previous packet, abort *)
          end (* if 'F' *)
        else if (ch = 'Z') then        (* end of file *)
          begin
            if (num <> (n mod 64)) then(* wrong packet, abort *)
              begin
                rdata := 'a';
                exit(rdata)
              end; (* if *)
            spack('Y',n mod 64,0,packet); (* ok, ACK it *)
            if (len = 1) and (recpkt[0] = 'D')
              then
                begin
                  debugwrite(concat('Discarding ',xfilename));
                  if f_is_binary               {discard the file}
                    then close(b_file)
                    else close(t_file)
                end
              else
                begin
                  debugwrite(concat('Closing ',xfilename));
                  if f_is_binary               (* close up the file *)
                    then
                      begin
                        if bufpos > 1               {data in last block}
                          then
                            begin
                              for dummy := bufpos to blksize do
                                filebuf[dummy] := chr(0);
                              dummy := blockwrite(b_file,filebuf,1);
                              {$B AFS+}
                              dummy := pred(bufpos);
                              did_attr := 
                                  put_attribute(b_file,FA_lastvalidbyte,dummy)
                              {$E AFS+}
                            end;
                        {$B AFS+}
                        with date_attr do if valid then {set date}
                          did_attr :=
                              put_attribute(b_file,FA_revisiondate,value);
                        {$E AFS+}
                        close(b_file,lock)
                      end
                    else
                      begin
                        {$B AFS+}
                        with date_attr do if valid then {set date}
                          did_attr := 
                              put_attribute(t_file,FA_creationdate,value);
                        {$E AFS+}
                        close(t_file,lock)
                      end;
                  {$B AFS-}
                  mark(heap);
                  if D_dirlist(xfilename,[D_code,D_text,D_data,D_svol],
                               this_file,false) <> D_okay
                    then {we have an error... should never occur}
                      begin
                        this_file := nil;
                        debugwrite('Can''t locate Unit containing File')
                      end
                    else if f_is_binary and (bufpos > 1) then
                      put_lastbyte(this_file^.dunit,xfilename,pred(bufpos));
                  debugdate;
                  with date_attr do if valid then {set date,time}
                    begin
                      case D_changedate(xfilename,value.date,
                           [D_code,D_text,D_data,D_svol]) of
                        D_okay :      debugwrite('Date set OK');
                        D_notfound :  debugwrite('No such File, Date not set');
                        D_nameerror : debugwrite('Name error, Date not set');
                        D_offline :   debugwrite('Volume offline, Date not set'
);
                        D_other :     debugwrite('Unknown error, Date not set')
;
                      end {case};
                      if this_file <> nil
                        then put_filetime(this_file^.dunit,xfilename,value.time
)
                    end;
                  debugdate;
                  release(heap);
                  {$E AFS-}
                end;
            bufpos := 1;                {clean up binary file buffer}
            n :=  n + 1;               (* bump packet counter *)
            currstate := 'f';              (* go to complete state *)
          end (* else if 'Z' *)
        else if (ch = 'E') then        (* error packet *)
          begin
            error(recpkt,len);         (* display error *)
            currstate := 'a'               (* and abort *)
          end (* if 'E' *)
        else if (ch <> chr(0)) then    (* some other packet type, *)
            currstate := 'a'               (* abort *)
    until (currstate <> 'd');
    rdata := currstate
  end; (* rdata *)

function rfile: char;

(* receive file header *)

var num, len: integer;
    ch: char;
    oldfn: string255;
    i: integer;

procedure makename(recpkt: packettype; var fn: string255; l: integer);

function exist(fn: string255): boolean;

(* returns true if file named fn exists *)

var f: file;
  
  begin
    (*$I-*) (* turn off i/o checking *)
    reset(f,fn);
    exist := (ioresult = 0);
    (*$I+*)
  end; (* exist *)

procedure checkname(var fn: string255);

(* if file fn exists, makes a new name which doesn't *)
(* does this by changing letters in file name until it *)
(* finds some combination which doesn't exitst *)

var ch: char;
    i: integer;

  begin
    i := 1;
    while (i <= length(fn)) and exist(fn) do
      begin
        ch := succ(fn[i]);    {RTC, 13 May 89}
        if not (ch in ['A'..'Z']) then ch := 'A';
        while (ch in ['A'..'Z']) and exist(fn) do
          begin
            fn[i] := ch;
            ch := succ(ch);
          end; (* while *)
        i := i + 1
      end; (* while *)
    end; (* checkname *)

  begin (* makename *)
    fn := copy('               ',1,15);    (* stretch length *)
    moveleft(recpkt[0],fn[1],l);           (* get filename from packet *)
    oldfn := copy(fn, 1,l);                (* save fn sent to show user *)
    fn := copy(fn,1,min(15,l));            (* set length of filename *)
                                           (* and make sure <= 15 *)
    uppercase(fn);
    if not f_is_binary then 
        if (pos('.TEXT',fn) <> length(fn)-4) or (length(fn) < 5) then
      begin
        if length(fn) > 10 then
            fn := copy(fn,1,10);           (* can only be 15 long in all *)
        fn := concat(fn,'.TEXT');          (* and we'll add .TEXT *)
      end; (* if *)
    if fwarn then                          (* if file warning is on *)
        checkname(fn);                     (* must check that name unique *)
  end; (* makename *)

  begin (* rfile *)
    debugwrite('rfile');

    if (numtry > maxtry) then         (* if too many tries, give up *)
      begin
        rfile := 'a';
        exit(rfile)
      end;
    numtry := numtry + 1;

    ch := rpack(len,num,recpkt);      (* receive a packet *)

    refresh_screen(numtry,n);

    if ch = 'S' then                  (* send init, maybe our ACK lost *)
      begin
        if (oldtry > maxtry) then     (* too many tries, abort *)
          begin
            rfile := 'a';
            exit(rfile)
          end; (* if *)

        if num = (pred(n) mod 64) then      (* previous packet mod 64? *)
          begin                       (* yes, ACK it again *)
            spar(packet);             (* with our send init params *)
            spack('Y',num,10,packet);
            numtry := 0;              (* reset try counter *)
            rfile := currstate;           (* stay in same state *)
          end (* if *)
        else                          (* not previous packet, abort *)
          rfile := 'a'
      end (* if 'S' *)
    else if (ch = 'Z') then           (* end of file *)
      begin
        if (oldtry > maxtry) then     (* too many tries, abort *)
          begin
            rfile := 'a';
            exit(rfile)
          end; (* if *)

        if num = (pred(n) mod 64) then       (* previous packet mod 64? *)
          begin                       (* yes, ACK it again *)
            spack('Y',num,0,packet);
            numtry := 0;
            rfile := currstate            (* stay in same state *)
          end (* if *)
        else
            rfile := 'a'              (* no, abort *)
      end (* else if *)
    else if (ch = 'F') then           (* file header *)
      begin                           (* which is what we really want *)
        if (num <> (n mod 64)) then   (* if wrong packet, abort *)
          begin
            rfile := 'a';
            exit(rfile)
          end;

        makename(recpkt,xfilename,len); (* get filename, make unique if filew *
)
        SC_erase_to_EOL(filepos,fileline);
        write(oldfn,' ==> ',xfilename);

        if not getfil(xfilename) then  (* try to open new file *)
          begin
            ioerror(ioresult);        (* if unsuccessful, tell them *)
            rfile := 'a';             (* and abort *)
            exit(rfile)
          end; (* if *)

        spack('Y',n mod 64,0,packet); (* ACK file header *)
        
        {initializations for file attribute data}
        date_attr.valid := false;
        {end of initializations for file attribute data}
        
        oldtry := numtry;             (* reset try counters *)
        numtry := 0;
        n := n + 1;                   (* bump packet number *)
        rfile := 'd';                 (* switch to data state *)
      end (* else if *)
    else if ch = 'B' then             (* break transmission *)
      begin
        if (num <> (n mod 64)) then            (* wrong packet, abort *)
          begin
            rfile := 'a';
            exit(rfile)
          end;
        spack('Y',n mod 64,0,packet); (* say ok *)
        rfile := 'c'                  (* go to complete state *)
      end (* else if *)
    else if (ch = 'E') then
      begin
        error(recpkt,len);
        rfile := 'a'
      end
    else if (ch = chr(0)) then        (* returned false *)
        rfile := currstate                (* so stay in same state *)
    else                              (* some weird state, so abort *)
        rfile := 'a'
  end; (* rfile *)

function rinit: char;

(* receive initialization *)

var num, len: integer;  (* packet number and length *)
    ch: char;
    fn : packettype;

  begin
    debugwrite('rinit');

    if (numtry > maxtry) then         (* if too many tries, give up *)
      begin
        rinit := 'a';
        exit(rinit)
      end;
    numtry := numtry + 1;
    
    if get_from_server then {ask server for files}
      begin
        len := length(xfilename);
        moveleft(xfilename[1],fn[0],len);
        spack('R', n mod 64, len, fn)
      end;

    ch := rpack(len,num,recpkt); (* receive a packet *)
    refresh_screen(num_try,n);

    if (ch = 'S') then           (* send init packet *)
      begin
        rpar(recpkt,len);            (* get other side's init data *)
        spar(packet);            (* fill packet with my init data *)
        ctl_set := [chr(0)..chr(31),chr(del),quote];
        if en_qbin then ctl_set := ctl_set + [qbin];
        spack('Y',n mod 64,10,packet); (* ACK with my params *)
        get_from_server := false;
        oldtry := numtry;        (* save old try count *)
        numtry := 0;             (* start a new counter *)
        n := n + 1;              (* bump packet number *)
        rinit := 'f';            (* enter file receive state *)
      end (* if 'S' *)
    else if ch = 'Y' then
      begin
        rinit := 'r';
        if n mod 64 = num then {we have the right ACK}
          begin
            get_from_server := false;
            numtry := 0;
            n := n + 1
          end
      end {if 'Y'}
    else if (ch = 'E') then
      begin
        rinit := 'a';
        error(recpkt,len)
      end (* if 'E' *)
    else if (ch = chr(0)) or (ch = 'N')  then
        rinit := 'r'             (* stay in same state *)
    else
        rinit := 'a'             (* abort *)
  end; (* rinit *)

(* state table switcher for receiving packets *)

  begin (* recswok *)
    rec_ok := false;
    writescreen('Receiving');
    currstate := 'r';            (* initial state is receive *)
    n := 0;                  (* set packet # *)
    numtry := 0;             (* no tries yet *)
    flush_comm;         {flush any garbage in buffer}

    while true do
        if currstate in ['d', 'f', 'r', 'c', 'a'] then
          case currstate of
              'd': currstate := rdata;
              'f': currstate := rfile;
              'r': currstate := rinit;
              'c': begin
                     rec_ok := true;
                     exit(recsw)
                   end; (* case c *)
              'a': exit(recsw)
            end (* case *)
        else (* state not in legal states *)
          begin
            debugwrite('Unknown State');
            exit(recsw)
          end (* else *)
  end; (* recsw *)

procedure rec_version;
  
  begin
    writeln(my_version)
  end {rec_version};

end. { receiver }
{>>>>}
