(* >>>> RECEIVER.TEXT *************************************************) $I- $R- $S+ $V- UNIT receiver; INTRINSIC CODE 25 ; INTERFACE USES kermglob, kermutil, kermpack; PROCEDURE recsw( VAR rec_ok: BOOLEAN ); IMPLEMENTATION PROCEDURE recsw{ var rec_ok: boolean }; var oldtry, numtry, spnum, rpnum, len : integer; ch : char; host_fname : string; ready : boolean; FUNCTION open_file( var fn : string ) : boolean; var i : integer; begin rewrite( rec_file , concat( prefix_vol, fn ) ); iostatus := ioresult; if iostatus = 0 then if text_file then begin fillchar( filebuf[1], page_size, chr(0) ); i := blockwrite( rec_file, filebuf[1], 2); iostatus := ioresult; if i <> 2 then io_status := 8; end; open_file := ( io_status = 0 ); bufpos := 1; crpos := page_size - 1; dle_flag := false; end; { open_file } FUNCTION close_file : boolean; var file_end, num_block, i : integer; begin if text_file then begin file_end := page_size; num_block := 2; end else begin file_end := blk_size; num_block := 1; end; fillchar( filebuf[bufpos], (file_end - bufpos), chr(0) ); i := blockwrite( rec_file, filebuf[1], num_block ); iostatus := ioresult; if i <> num_block then io_status := 8; close_file := ( io_status = 0 ); close( rec_file, lock ); end; { close_file } FUNCTION exist( var fn : string ) : boolean; begin reset( rec_file, concat( prefix_vol, fn ) ); exist := ( ioresult = 0 ); close( rec_file ) end; { exist } PROCEDURE check_name( var fn : string ); var ch : char; i : integer; begin i := 1; while ( i <= length( fn ) ) and exist( fn ) do begin ch := 'A'; while ( ch in [ 'A'..'Z' ] ) and exist( fn ) do begin fn[ i ] := ch; ch := succ( ch ); end; i := i + 1; end; end; { check_name } PROCEDURE make_name( var rpkt: packettype; var fn : string; len : integer ); change the received filename into a legal apple ucsd filename var i : integer; begin host_fname[0] := chr( min( 80, len ) ); moveleft( rpkt[0], host_fname[1], min( 80, len ) ); fn := copy( host_fname, 1, min( 15, len ) ); { take left part of received filename, max 15 long } uppercase( fn ); if text_file then begin if ( length(fn) < 5 ) or ( pos('.TEXT',fn) <> length(fn) - 4 ) then begin if length(fn) > 10 then fn := copy(fn,1,10); fn := concat( fn, '.TEXT' ); end; end; { add .TEXT if the expected file is a textfile } for i := 1 to length( fn ) do if fn[i] in [ chr(0)..chr(31),':','$',',','=','?','[' ] then fn[i] := 'X'; { replace apple ucsd incompatible char's in filename with 'X' } if fwarn then checkname( fn ); end; { make_name } FUNCTION rdata: char; (* send file data *) begin if debug then debug_write( 'rdata' ); repeat currstate := 'a'; if interrupt(int_key) or (numtry > maxtry) then begin rdata := 'a'; send_errpack( spnum ); exit( rdata ) end; num_try := num_try + 1; unitclear( inport ); ch := rpack(spnum, len, rpnum, recpkt, xtime, sohchar );{ receive a packet } refresh_screen( numtry, spnum ); if debug then ack_write( ch, len, rpnum, recpkt ); case ch of 'D' : { got data packet. if wrong packet number : abort. } { if previous packet : ack it again but not more than maxtry times } begin if spnum = rpnum then begin if text_file then bufemp_t( len ) else bufemp_i( len ); if io_status <> 0 then begin io_error( io_status ); send_errpack( spnum ); end else begin spack( 'Y', spnum, 4 ); numtry := 0; spnum := ( spnum + 65 ) mod 64; currstate := 'd'; end; end else begin if ( (spnum-1) mod 64 ) = rpnum then begin if oldtry > maxtry then begin rdata := 'a'; exit( rdata ); end; spack( 'Y', rpnum, 4 ); numtry := 0; oldtry := oldtry + 1; currstate := 'd'; end; end; end; { case 'D' } 'F' : { got file header packet again: if it was previous packet } { ack it again but not more than maxtry times. any other } { packet number : abort } begin if ( (spnum-1) mod 64 ) = rpnum then begin if oldtry > maxtry then begin rdata := 'a'; exit( rdata ); end; spack ( 'Y', rpnum, 4 ); numtry := 0; oldtry := oldtry + 1; currstate := 'd'; end; end; { case 'F' } 'E' : { error packet received : write it to screen and abort } error( recpkt, len ); '@' : { in case of receive failure send nack and stay in this state } begin spack( 'N', spnum, 4 ); currstate := 'd'; end; 'Z' : { end-of-file packet received : if it has the right packet number } { close the current file and go to rfile state to check whether } { another file haeder packet is coming or an end-of-transmission } { packet. } begin if spnum = rpnum then begin if debug then debugwrite( 'reof' ); if not close_file then begin io_error( io_status ); send_errpack( spnum ); end else begin spack( 'Y', spnum, 4 ); spnum := ( spnum + 65 ) mod 64; numtry := 0; oldtry := 0; currstate := 'f'; end; end; end; { case 'Z' } end; { case ch } until (currstate <> 'd'); rdata := currstate end; { rdata } FUNCTION rfile: char; (* receive file header *) begin (* rfile *) currstate := 'a'; (* set default state for rfile to abort *) if debug then debug_write( 'rfile' ); if interrupt(int_key) or (numtry > maxtry) then begin rfile := 'a'; send_errpack( spnum ); exit( rfile ) end; numtry := numtry + 1; unitclear( inport ); ch := rpack(spnum, len, rpnum, recpkt, xtime, sohchar); (* receive a packet *) refresh_screen( numtry, spnum ); if debug then ack_write( ch, len, rpnum, recpkt ); case ch of 'S' : { maybe our ack for packet 0 was lost: send it again, but not more } { than maxtry times } begin if ((spnum-1) mod 64) = rpnum then begin if oldtry > maxtry then begin rfile := 'a'; exit(rfile) end; spar; spack( 'Y', rpnum, 10 ); numtry := 0; oldtry := oldtry + 1; currstate := 'f'; { stay in same state } end; { for any other packet num: abort } end; { case 'S' } 'Z' : { maybe our ack for the eof packet was lost: ack it again } begin if ((spnum-1) mod 64) = rpnum then begin if oldtry > maxtry then begin rfile := 'a'; exit(rfile) end; spack( 'Y', rpnum, 4 ); numtry := 0; oldtry := oldtry + 1; currstate := 'f'; { stay in same state } end; { for any other packet num: abort } end; { case 'Z' } 'B' : { if the right packet num for the eot packet then ack it and go } { to the complete state; else abort } begin if spnum = rpnum then begin if debug then debug_write( 'rbreak' ); spack( 'Y', spnum, 4 ); currstate := 'c'; end; { if not the right num: abort } end; { case 'B' } '@' : { in case of receive failure send nack and stay in this state } begin spack( 'N', spnum, 4 ); currstate := 'f'; end; { case '@' } 'E' : { error packet received: write it on screen and abort } error( recpkt, len ); 'F' : { fileheader packet received which is what we really want: } { if not the right packetnumber : abort } { if a new file cannot be opened : send error packet to host and abort} { if new file is opened : go to receive data state } begin if spnum = rpnum then begin makename( recpkt, xfilename, len ); gotoxy( file_pos, file_line ); write( host_fname, ' ==> ', concat(prefix_vol, xfilename)); if not open_file( xfilename ) then begin io_error( io_status ); send_errpack( spnum ); end else begin spack( 'Y', spnum, 4 ); numtry := 0; oldtry := 0; spnum := ( spnum + 65 ) mod 64; currstate := 'd'; end; end; end; { case 'F' } end; { case ch } rfile := currstate; end; (* rfile *) FUNCTION rinit: char; (* receive initialization *) begin rinit := 'r'; { stay in 'r' in case reception failed: ptype = '@' } if debug then debug_write( 'rinit' ); if interrupt(int_key) or (numtry > init_try) then begin rinit := 'a'; send_errpack( spnum ); exit( rinit ) end; { too many tries : abort. inittry is five times maxtry in case other } { side waits before starting to send. } numtry := numtry + 1; unitclear( inport ); ch := rpack(spnum, len, rpnum, recpkt, mytime, sohchar);(* receive a packet *) refresh_screen(num_try, spnum); if debug then ack_write( ch, len, rpnum, recpkt ); if (ch = 'S') then (* send init packet *) begin rpar; (* get other side's init data *) spar; (* fill packet with my init data *) numtry := 0; (* start a new counter *) oldtry := 0; (* start oldtry for rfile *) spack( 'Y', spnum, 10 ); (* send my init parameters *) spnum := (spnum + 65) mod 64; (* bump packet number *) rinit := 'f'; (* enter file send state *) end { if 'S' } else if (ch <> '@') then (* abort for every other packet *) begin (* except when rec failed, then *) rinit := 'a'; if ch = 'E' then error( recpkt, len ) end else spack( 'N', spnum, 4); (* send a NACK packet *) end; (* rinit *) PROCEDURE RECSW (* state table switcher for receiving packets *) begin (* recsw *) unitclear(inport); writescreen('Receiving'); if not text_file then check_apple_char( no_mask_msbit_remin ); { for image transfer leave msbit unchanged } check_apple_char( sfb_char ); { restore action of ^S, ^F, ^@ keys during receive } ready := false; currstate := 'r'; (* initial state is send *) spnum := 0; (* set packet # *) numtry := 0; (* no tries yet *) while not ready do begin 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; ready := true; end; (* case c *) 'a': begin rec_ok := false; ready := true; end (* case a *) end (* case *) else (* state not in legal states *) begin rec_ok := false; ready := true; end; (* else *) end; { while } check_apple_char( mask_msbit_remin ); check_apple_char( no_sfb_char ); end; (* recsw *) begin end. { receiver }