|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 } {>>>>}