|x|*|f6|*|f5|*|f4|*|f3|*|f2|*|f1|{bootstrap the function keys}|. jff/Change log:/|nsm$log|nqan|{locate & mark the Change log}|. cfucpecan.p[begin,end]|n|f6ucpecan.p|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|f3|f3|f3|f3|f3|f3|f3|f3|f3|f37|n|*|f6|f3|{main extraction sequence}|. |xsmend|njfk/|d|e|f2|{extraction initialization, replaced by filename}|. jfd|n|eqa|{remove unwanted filename line}|. {>>>> KERMIT.TEXT} program kermit; (* $R-*) (* turn range checking off *) (* $L+*) USES {$u kermglob.code} kermglob, {$U kermutil.code} kermutil, {$U parser.code} parser, {$U helper.code} helper, {$U sender.code} sender, {$U receiver.code} receiver, {$U client.code} client; const my_version = 'Kermit-UCSD V1.1, 13 May 89'; {Change log: 13 May 89, V1.1: Fixed "lost debug file" bug RTC 30 Apr 89, V1.1: Moved set/show & connect procedures to kermutil RTC 30 Apr 89, V1.1: Added KERMENUS unit RTC 26 Apr 89, V1.1: Fixed "chained TAKE commands" bug RTC 19 Apr 89, V1.1: minor cleanups RTC 16 Apr 89, V1.1: Added BYE & FINISH commands RTC 15 Apr 89, V1.1: Added GET and PUT commands RTC 13 Apr 89, V1.1: Began work on new Version RTC 17 Aug 88: Misc. cleanup and bug fixes in LOG command RTC 14 Aug 88: Added LOG and CLOSE commands RTC 31 Jul 88: Modified for variable system_id RTC 02 Jul 88: Added Binary transfers & TAKE command RTC 29 Jun 88: Fixed Assorted Bugs in "connect" escape functions RTC Modifications by SP, 25 Oct 1983: adapt to IBM Version IV.1 Delete keyboard and serial buffering: provided by system already. Additional mods by SP, 18 Mar 1984: make all strings 255 chars long 13 May 84: Incorporate screen control through syscom record entries for portability } var taking_commands : boolean; procedure initialize; var ch: char; begin ker_version := my_version; writeln(ker_version); writeln( ' This program uses Library Units (c) 1986 Pecan Software Systems, Inc.'); writeln( ' This program may be freely distributed for non-commercial purposes.'); writeln; timint := mytime; pad := mypad; padchar := chr(mypchar); xeol := chr(my_eol); esc_char := chr(my_esc); quote := my_quote; ctlset := [chr(0)..chr(31),chr(del),quote]; half_duplex := false; debug := false; {$I-} rewrite(debf,'CONSOLE:'); {$I+} emulating := false; f_is_binary := false; lit_names := false; fwarn := false; spsiz := max_pack; rpsiz := max_pack; n := 0; parity := nopar; initvocab; fill_parity_array; ibm := false; xon := chr(17); bufpos := 1; bufend := 0; baud := defaultbaud; system_id := 'UNKNOWN'; if setup_comm then {baud was ok}; {$I-} reset(cmd_file,'*kermitinfo.text'); taking_commands := io_result = 0; if ioresult <> 0 then close(cmd_file) {$I+} end; (* initialize *) procedure closeup; begin close(debf,lock); page( output ) end; (* closeup *) begin (* main kermit program *) initialize; repeat write('Kermit-UCSD> '); if taking_commands then begin readln(cmd_file,line); writeln(line); if eof(cmd_file) then begin close(cmd_file); taking_commands := false end end else readstr(keyport,line); case parse of unconfirmed: writeln('Unconfirmed'); parm_expected: writeln('Parameter expected'); ambiguous: writeln('Ambiguous'); unrec: writeln('Unrecognized command'); fn_expected: writeln('File name expected'); ch_expected: writeln('Single character expected'); null: case verb of consym: connect; helpsym: help; logsym: begin {$I-} case adj of debugsym: begin close(debf,lock); rewrite(debf,xfilename) end; end {case adj}; if ioresult <> 0 then begin writeln('Unable to open ',xfilename); case adj of debugsym: begin close(debf); rewrite(debf,'CONSOLE:') end; end {case adj}; end else {$I+} case adj of debugsym: write(debf, ker_version,' -- Debug log...'); end end; closesym: begin {$I-} case adj of debugsym: close(debf,lock); end {case adj}; if ioresult <> 0 then begin writeln('Unable to close file'); end; case adj of debugsym: rewrite(debf,'CONSOLE:'); end {case adj}; {$I+} end; takesym : begin {$I-} if taking_commands then close(cmd_file); reset(cmd_file,xfilename); taking_commands := io_result = 0; if ioresult <> 0 then close(cmd_file) {$I+} end; getsym, recsym: begin recsw(rec_ok,verb = getsym); gotoxy(0,debugline); write(chr(bell)); if rec_ok then writeln('successful receive') else writeln('unsuccessful receive'); (*$I-*) (* set i/o checking off *) if f_is_binary then close(b_file) else close(t_file); (*$I+*) (* set i/o checking back on *) gotoxy(0,promptline); end; (* recsym *) putsym, sendsym: begin uppercase(xfilename); sendsw(send_ok); gotoxy(0,debugline); write(chr(bell)); if send_ok then writeln('successful send') else writeln('unsuccessful send'); (*$I-*) (* set i/o checking off *) if f_is_binary then close(b_file) else close(t_file); (*$I+*) (* set i/o checking back on *) gotoxy(0,promptline); end; (* sendsym *) finsym,byesym: begin case verb of finsym: line := 'F'; byesym: line := 'L'; end {case}; clientsw(send_ok,'G',line); gotoxy(0,debugline); write(chr(bell)); if send_ok then writeln('successful transaction') else writeln('unsuccessful transaction'); (*$I-*) (* set i/o checking off *) close(t_file); (*$I+*) (* set i/o checking back on *) gotoxy(0,promptline); end; {generic server command} setsym: set_parms; show_sym: show_parms; end; (* case verb *) end; (* case parse *) until (verb = exitsym) or (verb = quitsym); closeup end. (* kermit *) {>>>> SENDER.TEXT} {$D AFS-} { indicates to compile to run without Adv. File Sys.} unit sender; interface {Change log: 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+} {$U syslibr:wild.code} wild, {$U syslibr:dir.info.code} dirinfo; const my_version = ' Sender Unit V1.1, 13 May 89'; 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-} last_blksize := 512; {default as we can't determine it} {$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: integer; ch: char; got_attr : boolean; {$B AFS+} file_date : FA_chron; {$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); packet[0] := '#'; { creation date attribute } {$B AFS+} packet[1] := tochar(chr(12)); { length } 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-} packet[1] := tochar(chr(6)); { length } with this_file^.D_date do {$E AFS-} begin 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')); {$B AFS+} 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')) {$E AFS+} end; spack('A',n mod 64,{$B AFS+}14{$E AFS+} {$B AFS-}8{$E AFS-},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 state *) 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 around 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 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 syslibr:wild.code} wild, {$U syslibr:dir.info.code} dirinfo; {$E AFS-} const my_version = ' Receiver Unit V1.1, 18 May 89'; {$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-} D_daterec {$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 {$B AFS+},date,time{$E AFS+} 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')); {$B AFS+} 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; {$E AFS+} 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+} 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); dummy := pred(bufpos); {$B AFS+} 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-} debugdate; with date_attr do if valid then {set date} case D_changedate(xfilename,value, [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}; debugdate; {$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 } {>>>> CLIENT.TEXT} unit client; interface {Change log: 13 May 89, V1.1: Misc. cleanups to debug messages RTC 30 Apr 89, V1.1: Fixed failure to terminate 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: Adapted CLIENT Unit from RECEIVE Unit RTC } procedure clientsw(var cli_ok: boolean; ptype: char; data: string); procedure cli_version; implementation uses screenops, {RTC, 10 Jul 88} {$U kermglob.code} kermglob, {$U kermutil.code} kermutil, {$U kermpack.code} kermpack; const my_version = ' Client Unit V1.1, 13 May 89'; var f_save : boolean; { save area for f_is_binary } procedure clientsw{(var cli_ok: boolean; ptype: char; data: string)}; function cdata: char; (* client text data *) var dummy, num, len: integer; ch: char; i: integer; begin repeat debugwrite('cdata'); if numtry > maxtry then begin currstate := 'a'; exit(cdata) 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 cdata := 'a'; (* too many tries, abort *) exit(cdata) 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 = 'X') then (* text header *) begin if (oldtry > maxtry) then begin cdata := 'a'; (* too many tries, abort *) exit(cdata) 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 'X' *) else if (ch = 'Z') then (* end of file *) begin if (num <> (n mod 64)) then(* wrong packet, abort *) begin cdata := 'a'; exit(cdata) end; (* if *) spack('Y',n mod 64,0,packet); (* ok, ACK it *) close(t_file); 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'); cdata := currstate end; (* cdata *) function cfile: char; (* client text header *) var num, len: integer; ch: char; i: integer; begin (* cfile *) debugwrite('cfile'); if (numtry > maxtry) then (* if too many tries, give up *) begin cfile := 'a'; exit(cfile) 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 cfile := 'a'; exit(cfile) 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 *) cfile := currstate; (* stay in same state *) end (* if *) else (* not previous packet, abort *) cfile := 'a' end (* if 'S' *) else if (ch = 'Z') then (* end of file *) begin if (oldtry > maxtry) then (* too many tries, abort *) begin cfile := 'a'; exit(cfile) 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; cfile := currstate (* stay in same state *) end (* if *) else cfile := 'a' (* no, abort *) end (* else if *) else if (ch = 'X') then (* text header *) begin (* which is what we really want *) if (num <> (n mod 64)) then (* if wrong packet, abort *) begin cfile := 'a'; exit(cfile) end; if not getfil('console:') then { try to open console output } begin ioerror(ioresult); { if unsuccessful, tell them } cfile := 'a'; { and abort } exit(cfile) end; spack('Y',n mod 64,0,packet); (* ACK file header *) oldtry := numtry; (* reset try counters *) numtry := 0; n := n + 1; (* bump packet number *) cfile := '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 cfile := 'a'; exit(cfile) end; spack('Y',n mod 64,0,packet); (* say ok *) cfile := 'c' (* go to complete state *) end (* else if *) else if (ch = 'E') then begin error(recpkt,len); cfile := 'a' end else if (ch = chr(0)) then (* returned false *) cfile := currstate (* so stay in same state *) else (* some weird state, so abort *) cfile := 'a' end; (* cfile *) function cinit: char; (* client initialization *) var num, len: integer; (* packet number and length *) ch: char; cmdpkt : packettype; begin debugwrite('cinit'); if (numtry > maxtry) then (* if too many tries, give up *) begin cinit := 'a'; exit(cinit) end; numtry := numtry + 1; len := length(data); moveleft(data[1],cmdpkt[0],len); spack(ptype, n mod 64, len, cmdpkt); 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 *) oldtry := numtry; (* save old try count *) numtry := 0; (* start a new counter *) n := n + 1; (* bump packet number *) cinit := 'f'; (* enter file receive state *) end (* if 'S' *) else if ch = 'Y' then begin cinit := 'c'; if n mod 64 = num then {we have the right ACK} begin numtry := 0; n := n + 1 end end {if 'Y'} else if (ch = 'N') then cinit := 'r' else if (ch = 'E') then begin cinit := 'a'; error(recpkt,len) end (* if 'E' *) else if (ch = chr(0)) then cinit := 'r' (* stay in same state *) else cinit := 'a' (* abort *) end; (* cinit *) (* state table switcher for receiving packets *) begin (* clientsw *) cli_ok := false; writescreen('Talking to Server'); f_save := f_is_binary; {save for later restore} f_is_binary := false; {client ONLY recieves text} 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 := cdata; 'f': currstate := cfile; 'r': currstate := cinit; 'c': begin f_is_binary := f_save; cli_ok := true; exit(clientsw) end; (* case c *) 'a': begin f_is_binary := f_save; exit(clientsw) end (* case a *) end (* case *) else (* state not in legal states *) begin debugwrite('Unknown State'); f_is_binary := f_save; exit(clientsw) end (* else *) end; (* clientsw *) procedure cli_version; begin writeln(my_version) end {cli_version}; end. { client } {>>>> HELPER.TEXT} unit helper; interface {Change log: 13 May 89, V1.1: Added SET INTERFACE, COMMENT, and "client" helps RTC 26 Apr 89, V1.1: minor cleanups RTC 13 Apr 89, V1.1: Added Version message RTC 14 Aug 88: Added command helps for SET SYSTEM command RTC 14 Aug 88: Added LOG and CLOSE help commands RTC 31 Jul 88: Minor cleanups of help messages RTC 30 Jun 88: Added -NAMES, -TYPE, and TAKE command helps RTC } procedure help; procedure hlp_version; implementation uses {$U kermglob.code} kermglob; const my_version = ' Helper Unit V1.1, 13 May 89'; procedure keypress; var ch: char; begin write('---------------Press any key to continue---------------'); read( keyboard, ch ); page(output); {SP} end; (* keypress *) procedure help1; var ch: char; begin { help1 } if (noun = nullsym) then begin writeln('KERMIT is a family of programs that do reliable file transfer'); writeln('between computers over TTY lines.', ' KERMIT can also be used to make the '); writeln('microcomputer behave as a terminal', ' for a mainframe. These are the '); writeln('commands for the UCSD p-System version, KERMIT-UCSD:'); writeln end; (* if *) if (noun = nullsym) or (noun = consym) then begin writeln(' CONNECT To make a "virtual terminal" connection to a remote'); writeln('':14, 'system.'); writeln; writeln('':14, 'To break the connection and "escape" back to the micro,'); writeln('':14, 'type the escape sequence (CTRL-] C, that is Control '); writeln('':14, 'rightbracket followed immediately by the letter C.)'); writeln; end; (* if *) if (noun = nullsym) or (noun = exitsym) then begin writeln(' EXIT To return back to main command level of the p-system.'); end; (* if *) if (noun = nullsym) or (noun = quitsym) then begin writeln(' QUIT Same as EXIT.'); writeln; end; (* if *) if (noun = nullsym) or (noun = helpsym) then begin writeln(' HELP To get a list of KERMIT commands.'); writeln; end; (* if *) if (noun = nullsym) or (noun = recsym) then begin writeln(' RECEIVE To accept a file from the remote system.'); end; (* if *) if (noun = nullsym) or (noun = sendsym) then begin writeln(' SEND To send a file or group of files to the remote system.'); end; (* if *) if (noun = nullsym) or (noun = getsym) then begin writeln(' GET To request a file from a remote Kermit in SERVER mode.'); end; (* if *) if (noun = nullsym) or (noun = putsym) then begin writeln(' PUT To send a file to a remote Kermit in SERVER mode.'); writeln; end; (* if *) if (noun = nullsym) or (noun = byesym) then begin writeln(' BYE Shutdown and logout a remote Kermit in SERVER mode.'); end; (* if *) if (noun = nullsym) or (noun = finsym) then begin writeln(' FINISH Shutdown a remote Kermit in SERVER mode.'); end; (* if *) if (noun = nullsym) then keypress; end; (* help1 *) procedure help2; var ch: char; begin { help2 } if (noun = nullsym) or (noun = setsym) then begin writeln(' SET To establish system-dependent parameters. The '); writeln('':14, 'SET options are as follows: '); writeln; if (adj = nullsym) or (adj = debugsym) then begin writeln('':14, 'DEBUG To set debug mode ON or OFF '); writeln('':31, '(default is OFF).'); writeln; end; (* if *) if (adj = nullsym) or (adj = escsym) then begin writeln('':14, 'ESCAPE To change the escape sequence that '); writeln('':31, 'lets you return to the PC Kermit from'); writeln('':31, 'the remote host. The default is CTRL-] c.'); writeln; end; (* if *) if (adj = nullsym) or (adj = filenamsym) then begin writeln('':14, 'FILE-NAMES LITERAL/CONVERTED, Default is CONVERTED, '); writeln('':31, 'In this Kermit LITERAL Names have'); writeln('':31, 'Volume name Stripped, while CONVERTED'); writeln('':31, 'Names also have all but the final'); writeln('':31, '''.'' removed.'); writeln; end; (* if *) if (adj = nullsym) or (adj = filetypesym) then begin writeln('':14, 'FILE-TYPE BINARY/TEXT Default is TEXT.'); writeln; end; (* if *) if (adj = nullsym) or (adj = filewarnsym) then begin writeln('':14, 'FILE-WARNING ON/OFF, default is OFF. If ON, '); writeln('':31, 'Kermit will warn you and rename an incoming '); writeln('':31, 'file so as not to write over a file that '); writeln('':31, 'currently exists with the same name'); writeln; end; (* if *) if (adj = nullsym) then keypress; end; (* if *) end; (* help2 *) procedure help3; begin if (noun = nullsym) or (noun = setsym) then begin if (adj = nullsym) or (adj = baudsym) then begin writeln('':14, 'BAUD To set the serial baud rate.' ); writeln('':31, 'Choices are dependant on your Hardware.' ); writeln('':31, 'The default is 1200.'); writeln; end; (* if *) if (adj = nullsym) or (adj = ibmsym) then begin writeln('':14, 'IBM ON/OFF, default is OFF. This flag '); writeln('':31, 'should be ON only when transfering files'); writeln('':31, 'between the micro and an IBM VM/CMS'); writeln('':31, 'system. It also causes the parity to'); writeln('':31, 'be set appropriately (mark) and activates'); writeln('':31, 'local echoing'); writeln; end; (* if *) if (adj = nullsym) or (adj = intsym) then begin writeln('':14, 'INTERFACE KERMIT/UCSD, default is KERMIT.'); writeln('':31, 'Permits selection of prefered User Interface:'); writeln('':31, 'KERMIT command line or UCSD menus.'); writeln; end; (* if *) if (adj = nullsym) or (adj = localsym) then begin writeln('':14, 'LOCAL-ECHO ON/OFF, default is OFF. This sets the'); writeln('':31, 'duplex. It should be ON when using '); writeln('':31, 'the IBM and OFF for the DEC-20.'); writeln; end; (* if *) if (adj = nullsym) or (adj = emulatesym) then begin writeln('':14, 'EMULATE ON/OFF, default is OFF. This sets the'); writeln('':31, 'DataMedia 1520A terminal emulation on or off.'); writeln; end; (* if *) if (adj = nullsym) then keypress; end; (* if *) end; (* help3 *) procedure help4; begin if (noun = setsym) or (noun = nullsym) then begin if (adj = nullsym) or (adj = systemsym) then begin writeln('':14, 'SYSTEM-ID Specify the System-ID for your REMUNIT'); writeln('':31, 'if your REMUNIT needs it specified.'); writeln('':31, 'Called "model" in the REMUNIT specs.'); writeln('':31, 'Default System-ID is UNKNOWN'); writeln; end; (* if *) if (adj = nullsym) or (adj = paritysym) then begin writeln('':14, 'PARITY EVEN, ODD, MARK, SPACE, or NONE.'); writeln('':31, 'NONE is the default but if the IBM '); writeln('':31, 'flag is set, parity is set to MARK. '); writeln('':31, 'This flag selects the parity for '); writeln('':31, 'outgoing and incoming characters during'); writeln('':31, 'CONNECT and file transfer to match the'); writeln('':31, 'requirements of the host.'); writeln; end; (* if *) end; (* if *) if (noun = nullsym) or (noun = showsym) then begin writeln(' SHOW To see the values of parameters that can be modified'); write('':14, 'via the SET command. '); if (adj in [paritysym, localsym, ibmsym, escsym, debugsym, filenamsym, filetypesym, filewarnsym, baudsym, emulatesym, systemsym, nullsym]) then begin writeln('For an explanation of the parameter,'); writeln('':14, 'see the help for the matching SET command.'); write('':14) end; (* if *) if (adj in [allsym, versionsym, nullsym]) then begin writeln('Additional SHOW options are as follows:'); end; (* if *) writeln; if (adj = nullsym) or (adj = allsym) then begin writeln('':14, 'ALL Show all parameters.'); writeln; end; (* if *) if (adj = nullsym) or (adj = versionsym) then begin writeln('':14, 'VERSION Show version information.'); writeln; end; (* if *) end; (* if *) if (noun = nullsym) then keypress; if (noun = nullsym) or (noun = takesym) then begin writeln(' TAKE This command instructs Kermit to take further'); writeln('':14, 'commands from a specified file.'); end; (* if *) if (noun = nullsym) or (noun = comsym) then begin writeln(' COMMENT Comments a TAKE file. (ignored)'); writeln; end; (* if *) if (noun = nullsym) or (noun = logsym) then begin writeln(' LOG This command opens a selected log file.'); writeln('':14, 'LOG options are as follows:'); writeln; if (adj = nullsym) or (adj = debugsym) then begin writeln('':14, 'DEBUG open specified file for debug output.'); writeln; end; (* if *) end; (* if *) if (noun = nullsym) or (noun = closesym) then begin writeln(' CLOSE This command closes a selected log file previously'); writeln('':14, 'opened via the LOG command.'); end; (* if *) end; (* help4 *) procedure help; begin help1; help2; help3; help4 end; (* help *) procedure hlp_version; begin writeln(my_version) end {hlp_version}; end. { unit helper } {>>>> PARSER.TEXT} (*$S+*) unit parser; INTERFACE uses {$U kermglob.code} kermglob; {Change log: 13 May 89, V1.1: Fixed several bugs in parsing of HELP commands RTC 13 May 89, V1.1: Added parsing for COMMENT command 30 Apr 89, V1.1: Added parsing for SET INTERFACE command RTC 26 Apr 89, V1.1: minor cleanups RTC 16 Apr 89, V1.1: Added BYE & FINISH command parsing RTC 14 Apr 89, V1.1: Added parsing for GET, PUT & SHOW VERSION commands RTC 13 Apr 89, V1.1: Added Version message RTC 14 Aug 88: Added parsing for LOG, CLOSE, and SET SYSTEM commands RTC 02 Jul 88: Added -NAMES, -TYPE, TAKE command parsing RTC } function parse: statustype; procedure initvocab; procedure par_version; IMPLEMENTATION uses {$U kermutil.code} kermutil; const my_version = ' Parser Unit V1.1, 13 May 89'; procedure eatspaces(var s: string255); var done: boolean; i: integer; begin done := (length(s) = 0); while not done do begin if s[1] = ' ' then begin i := length(s) - 1; s := copy(s,2,i); done := length(s) = 0 end (* if *) else done := true end (* while *) end; (* eatspaces *) procedure isolate_word(var line, s: string255); var i: integer; done: boolean; begin done := false; i := 1; s := copy(' ',0,0); while (i <= length(line)) and not done do begin if line[i] = ' ' then done := true else s := concat(s,copy(line,i,1)); i := i + 1; end; (* while *) line := copy(line,i,length(line)-i+1); end; (* isolate_word *) function get_fn(var line, fn: string255): boolean; var i, l: integer; begin get_fn := true; isolate_word(line, fn); l := length(fn); if (l < 1) then get_fn := false end; (* get_fn *) function get_num( var line: string255; var n: integer ): boolean; var numstr: string255; i, l: integer; begin get_num := true; isolate_word( line, numstr ); l := length(numstr); if (l>5) or (l<1) then begin n := 0; get_num := false end else begin n := 0; i := 1; numstr := concat( numstr, ' ' ); while (numstr[i] in ['0'..'9']) do begin if n<(maxint div 10) then n := n*10 + ord( numstr[i] ) - ord( '0' ); i := i + 1 end end end; { get_num } function nextch(var ch: char): boolean; var s: string255; begin isolate_word(line,s); if length(s) <> 1 then nextch := false else begin ch := s[1]; nextch := true end (* else *) end; (* nextch *) function parse(*: statustype*); type states = (start, fin, get_filename, get_set_parm, get_parity, get_on_off, get_f_type, get_char, get_show_parm, get_help_show, get_int_type, get_naming, get_help_parm, exitstate, get_baud, get_line, get_log_parm, get_help_log); var status: statustype; word: vocab; state: states; function get_a_sym(var word: vocab): statustype; var i: vocab; s: string255; stat: statustype; done: boolean; matches: integer; begin eat_spaces(line); if length(line) = 0 then get_a_sym := ateol else begin stat := null; done := false; isolate_word(line,s); i := allsym; matches := 0; repeat if (pos(s,vocablist[i]) = 1) and (i in expected) then begin matches := matches + 1; word := i end else if (s[1] < vocablist[i,1]) then done := true; if (i = versionsym) then done := true else i := succ(i) until (matches > 1) or done; if matches > 1 then stat := ambiguous else if (matches = 0) then stat := unrec; get_a_sym := stat end (* else *) end; (* get_a_sym *) begin state := start; parse := null; noun := nullsym; verb := nullsym; adj := nullsym; uppercase(line); repeat case state of start: begin expected := [comsym, consym, exitsym, helpsym, quitsym, logsym, closesym, getsym, putsym, byesym, finsym, recsym, sendsym, setsym, showsym, takesym]; status := get_a_sym(verb); if status = ateol then begin parse := null; exit(parse) end (* if *) else if (status <> unrec) and (status <> ambiguous) then case verb of comsym: state := get_line; consym, exitsym, quitsym, byesym, finsym, recsym: state := fin; getsym, putsym, sendsym, takesym: state := getfilename; helpsym: state := get_help_parm; logsym, closesym: state := get_log_param; setsym: state := get_set_parm; showsym: state := get_show_parm; end (* case *) end; (* case start *) fin: begin expected := []; status := get_a_sym(verb); if status = ateol then begin parse := null; exit(parse) end (* if status *) else status := unconfirmed end; (* case fin *) getfilename: begin expected := []; if getfn(line,xfilename) then begin status := null; state := fin end (* if *) else status := fnexpected end; (* case get file name *) get_set_parm: begin expected := [paritysym, localsym, ibmsym, emulatesym, escsym, debugsym, filenamsym, filetypesym, intsym, filewarnsym, baudsym, systemsym]; status := get_a_sym(noun); if status = ateol then status := parm_expected else if (status <> unrec) and (status <> ambiguous) then case noun of paritysym: state := get_parity; localsym: state := get_on_off; ibmsym: state := get_on_off; emulatesym: state := get_on_off; escsym: state := getchar; debugsym: state := get_on_off; filenamsym : state := get_naming; filetypesym : state := get_f_type; filewarnsym: state := get_on_off; intsym: state := get_int_type; baudsym: state := get_baud; systemsym: state := get_line end (* case *) end; (* case get_set_parm *) get_log_parm: begin expected := [debugsym]; status := get_a_sym(adj); if status = ateol then status := parm_expected else if (status <> unrec) and (status <> ambiguous) then if verb = logsym then state := getfilename else state := fin end; (* case get_log_parm *) get_line: begin eat_spaces(line); parse := null; exit(parse) end; {case get_line} get_parity, get_naming, get_int_type, get_on_off, get_f_type: begin case state of get_parity: expected := [marksym, spacesym, nonesym, evensym, oddsym]; get_naming: expected := [convsym, litsym]; get_int_type: expected := [kermitsym, ucsdsym]; get_on_off: expected := [onsym, offsym]; get_f_type: expected := [binsym, textsym]; end {case state}; status := get_a_sym(adj); if status = ateol then status := parm_expected else if (status <> unrec) and (status <> ambiguous) then state := fin end; (* case get_parity *) get_baud: begin expected := []; if get_num( line, newbaud ) then begin status := null; state := fin end else begin newbaud := 0; status := parm_expected end end; (* case get_baud *) get_char: if nextch(newescchar) then state := fin else status := ch_expected; get_show_parm: begin expected := [allsym, paritysym, localsym, ibmsym, emulatesym, escsym, debugsym, filenamsym, filetypesym, filewarnsym, baudsym, systemsym, versionsym]; status := get_a_sym(noun); if status = ateol then status := parm_expected else if (status <> unrec) and (status <> ambiguous) then state := fin end; (* case get_show_parm *) get_help_show, get_help_log: begin case noun of logsym, closesym: expected := [debugsym]; setsym: expected := [paritysym, localsym, ibmsym, escsym, intsym, debugsym, filenamsym, filetypesym, filewarnsym, baudsym, emulatesym, systemsym]; showsym: expected := [paritysym, localsym, ibmsym, escsym, debugsym, filenamsym, filetypesym, filewarnsym, baudsym, emulatesym, systemsym, allsym, versionsym]; end {case noun}; status := get_a_sym(adj); if (status = at_eol) then begin status := null; state := fin end else if (status <> unrec) and (status <> ambiguous) then state := fin end; (* case get_help_show *) get_help_parm: begin expected := [consym, exitsym, helpsym, quitsym, recsym, comsym, getsym, putsym, byesym, finsym, takesym, logsym, closesym, sendsym, setsym, showsym]; status := get_a_sym(noun); if status = ateol then begin parse := null; exit(parse) end; if (status <> unrec) and (status <> ambiguous) then case noun of consym, comsym, getsym, putsym, sendsym, finsym, byesym, takesym, recsym: state := fin; closesym, logsym: state := get_help_log; showsym, setsym: state := get_help_show; helpsym: state := fin; exitsym, quitsym: state := fin; end (* case *) end; (* case get_help_show *) end (* case *) until (status <> null); parse := status end; (* parse *) procedure initvocab; var i: integer; begin vocablist[allsym] := 'ALL'; vocablist[baudsym] := 'BAUD'; vocablist[binsym] := 'BINARY'; vocablist[byesym] := 'BYE'; vocablist[closesym] := 'CLOSE'; vocablist[comsym] := 'COMMENT'; vocablist[consym] := 'CONNECT'; vocablist[convsym] := 'CONVERTED'; vocablist[debugsym] := 'DEBUG'; vocablist[emulatesym] := 'EMULATE'; vocablist[escsym] := 'ESCAPE'; vocablist[evensym] := 'EVEN'; vocablist[exitsym] := 'EXIT'; vocablist[filenamsym] := 'FILE-NAMES'; vocablist[filetypesym] := 'FILE-TYPE'; vocablist[filewarnsym] := 'FILE-WARNING'; vocablist[finsym] := 'FINISH'; vocablist[getsym] := 'GET'; vocablist[helpsym] := 'HELP'; vocablist[ibmsym] := 'IBM'; vocablist[intsym] := 'INTERFACE'; vocablist[kermitsym] := 'KERMIT'; vocablist[litsym] := 'LITERAL'; vocablist[localsym] := 'LOCAL-ECHO'; vocablist[logsym] := 'LOG'; vocablist[marksym] := 'MARK'; vocablist[nonesym] := 'NONE'; vocablist[oddsym] := 'ODD'; vocablist[offsym] := 'OFF'; vocablist[onsym] := 'ON'; vocablist[paritysym] := 'PARITY'; vocablist[putsym] := 'PUT'; vocablist[quitsym] := 'QUIT'; vocablist[recsym] := 'RECEIVE'; vocablist[sendsym] := 'SEND'; vocablist[setsym] := 'SET'; vocablist[showsym] := 'SHOW'; vocablist[spacesym] := 'SPACE'; vocablist[systemsym] := 'SYSTEM-ID'; vocablist[takesym] := 'TAKE'; vocablist[textsym] := 'TEXT'; vocablist[ucsdsym] := 'UCSD'; vocablist[versionsym] := 'VERSION'; end; (* initvocab *) procedure par_version; begin writeln(my_version) end {par_version}; end. (* end of unit *) {>>>> INTFUTIL.TEXT} interface {Change log: 30 Apr 89, V1.1: Extracted from KERMUTIL RTC } uses {$U kermglob.code} kermglob; procedure fill_parity_array; procedure set_parms; procedure show_parms; procedure connect; function read_ch(unitno: integer; var ch: char): boolean; procedure read_str(unitno:integer; var s: string255); procedure echo(ch: char); procedure clear_buf(unitno:integer); function aand(x,y: integer): integer; function aor(x,y: integer): integer; function xor(x,y: integer): integer; procedure uppercase(var s: string255); procedure error(p: packettype; len: integer); procedure io_error(i: integer); procedure debugwrite(s: string255); procedure debugint(s: string255; i: integer); function min(x,y: integer): integer; function tochar(ch: char): char; function unchar(ch: char): char; function ctl(ch: char): char; function getch(var r: char): boolean; function getsoh: boolean; function getfil(filename: string255): boolean; procedure send_brk; function setup_comm : boolean; {changed 31 Jul 88, RTC} procedure flush_comm; {added 16 Apr 89, RTC} procedure write_bool(s: string255; b: boolean); procedure write_ch(unitno: integer; ch: char ); procedure writescreen(s: string255); procedure refresh_screen(numtry, num: integer); procedure set_timer(t : integer); {added 26 Apr 89, RTC} function timeout : boolean; {added 26 Apr 89, RTC} procedure utl_version; implementation {>>>> FAKEUTIL.TEXT} unit kermutil; { Change log: 30 Apr 89, V1.1: Created Fake version of KERMUTIL RTC } {$I intfutil.text} procedure fill_parity_array; begin end; (* fill_parity_array *) procedure write_bool{s: string255; b: boolean}; begin end; (* write_bool *) procedure show_parms; begin end; (* show_sym *) procedure set_parms; begin end; (* set_parms *) procedure connect; begin (* connect *) end; (* connect *) procedure uppercase(*var s: string255*); begin end; (* uppercase *) function read_ch(*unitno:integer; var ch: char): boolean*); begin end; (* read_ch *) procedure write_ch(*unitno: integer; ch: char*); begin end; procedure read_str(*unitno:integer; var s: string255*); begin end; (* read_str *) procedure clear_buf(*unitno:integer*); begin end; procedure send_brk; begin end; function setup_comm{ : boolean}; begin end; procedure flush_comm; {added 16 Apr 89, RTC} begin {flush_comm} end {flush_comm}; function aand(*x,y: integer): integer*); begin end; (* aand *) function aor(*x,y: integer): integer*); begin end; (* aor *) function xor(*x,y: integer): integer*); begin end; (* xor *) procedure error(*p: packettype; len: integer*); begin end; (* error *) procedure io_error(*i: integer*); begin end; (* io_error *) procedure debugwrite(*s: string255*); begin end; (* debugwrite *) procedure debugint(*s: string255; i: integer*); begin end; (* debugint *) function min(*x,y: integer): integer*); begin end; (* min *) function tochar(*ch: char): char*); begin end; (* tochar *) function unchar(*ch: char): char*); begin end; (* unchar *) function ctl(*ch: char): char*); begin end; (* ctl *) procedure echo(*ch: char*); begin end; (* echo *) function getch(*var r: char): boolean*); begin end; (* getch *) function getsoh(*: boolean*); begin end; (* getsoh *) function getfil(*filename: string255): boolean*); begin end; (* getfil *) procedure writescreen(*s: string255*); begin end; (* writescreen *) procedure refresh_screen(*numtry, num: integer*); begin end; (* refresh_screen *) procedure set_timer{t : integer}; {added 26 Apr 89, RTC} begin {set_timer} end {set_timer}; function timeout {: boolean}; {added 26 Apr 89, RTC} begin {timeout} end {timeout}; procedure utl_version; begin end {utl_version}; begin { body of unit kermutil } { initialization code } ***; { termination code } end. { fakeutil } {>>>> KERMUTIL.TEXT} {$D OS_ERHDL+} { indicates to compile to use Pecan's errorhandler unit } {$D OS_TIMER+} { indicates to compile to use TIME() for timeouts } unit kermutil; { Change log: 13 May 89, V1.1: Eliminated "int_bool_rec" & misc cleanups RTC 30 Apr 89, V1.1: Moved set/show & connect from kermit to here RTC 26 Apr 89, V1.1: Added support for TIMEr controlled timeouts RTC 16 Apr 89, V1.1: Added procedure flush_comm to Flush REMOTE: RTC 13 Apr 89, V1.1: Added Version message RTC 17 Aug 88: Fixed missing EOLN's problem in debf RTC 14 Aug 88: Fixed the debug messages to all go to debf RTC 31 Jul 88: Modified setup_comm to funct., updated io_error. RTC 10 Jul 88: Converted to using screenops unit RTC 02 Jul 88: Misc cleanup, eliminated char_int_rec, etc. RTC 26 Jun 88 Patched Unitwrite problem in Echo RTC 26 Jun 88 Modified read_ch to use cr_getkb RTC 13 May 84: Use KERNEL's syscom record for screen control -sp- } {$I intfutil.text} uses {$U *system.library} screenops, {RTC, 10 Jul 88} {$U kermenus.code} kermenus, {$U kermpack.code} kermpack (pak_version), {$U helper.code} helper (hlp_version), {$U parser.code} parser (par_version), {$U sender.code} sender (sen_version), {$U receiver.code} receiver (rec_version), {$U client.code} client (cli_version), {$U remunit.code} remunit, {SP, 1/14/84} {$U syslibr:kernel.code} kernel (syscom,version) {$B OS_ERHDL+}, {$U syslibr:errorhandl.code} error_handling {$E OS_ERHDL+}; const my_version = ' Kermutil Unit V1.1, 13 May 89'; type time_value = integer[10]; var old_flush, old_stop: char; time_limit : time_value; {$I setshow.text} procedure connect; (* connect to remote host and transceive *) var ch: char; close: boolean; procedure read_esc; (* read character after esc char and interpret it *) begin repeat until read_ch(keyport,ch); (* wait until they've typed something in *) if (ch in ['a'..'z']) then (* uppercase it *) ch := chr(ord(ch) - ord('a') + ord('A')); if ch in ['B','C','S','?'] then case ch of 'B': sendbrk; (* B: send a break to the IBM *) 'C': close := true; (* C: end connection *) 'S': begin (* S: show status *) noun := allsym; showparms end; (* S *) '?': begin (* ?: show options *) writeln ('B Send a BREAK signal.'); writeln ('C Close Connection, return to KERMIT-UCSD command level.'); writeln ('S Show Status of connection'); writeln ('? Print this list'); writeln ('^',ctl(esc_char),' send the escape character itself to the remote host.') end; (* ? *) end (* case *) else if ch = esc_char then (* ESC-char: send it out *) begin if half_duplex then write(ch); { changed from echo() by SP } write_ch(oport,ch) end (* else if *) else (* anything else: ignore *) write(chr(bell)) end; (* read_esc *) begin (* connect *) clear_buf(keyport); (* empty keyboard buffer *) clear_buf(inport); (* empty remote input buffer *) writeln('Connecting to host...type CTRL-',ctl(esc_char),' C to exit'); close := false; repeat if read_ch(inport,ch) then (* if char from host then *) echo(ch); (* echo it *) if read_ch(keyport,ch) then (* if char from keyboard then *) if ch <> esc_char then (* if not ESC-char then *) begin if half_duplex then (* echo it if half-duplex *) write(ch); { changed from echo() by sp } write_ch(oport,ch) (* send it out the port *) end (* if *) else (* ch = esc_char *) (* else is ESC-char so *) read_esc; (* interpret next char *) until close; (* if still connected, get more *) writeln('Disconnected') end; (* connect *) procedure uppercase(*var s: string255*); var i: integer; begin for i := 1 to length(s) do if s[i] in ['a'..'z'] then s[i] := chr(ord(s[i]) - ord('a') + ord('A')) end; (* uppercase *) function read_ch(*unitno:integer; var ch: char): boolean*); (* read a character from an input queue *) var ready: boolean; begin if unitno=keyport then ready := cr_kbstat else if unitno=inport then ready := cr_remstat else ready := false; if ready then (* if a char there *) if unitno=keyport then ch := cr_getkb else ch := cr_getrem; read_ch := ready end; (* read_ch *) procedure write_ch(*unitno: integer; ch: char*); begin if unitno=oport then cr_putrem( ch ) end; procedure read_str(*unitno:integer; var s: string255*); (* acts like readln(s) but takes input from input queue *) var i: integer; begin i := 0; s := copy('',0,0); repeat repeat (* get a character *) until read_ch(unitno,ch); if (ord(ch) = backspace) then (* if it's a backspace then *) begin if (i > 0) then (* if not at beginning of line *) begin write(ch); (* go back a space on screen *) write(' '); (* erase char on screen *) write(ch); (* go back a space again *) i := i - 1; (* adjust string counter *) s := copy(s,1,i) (* adjust string *) end (* if *) end (* if *) else if (ord(ch) <> eoln_sym) then (* otherwise if not at eoln then *) begin write(ch); (* echo char on screen *) i := i + 1; (* inc string counter *) s := concat(s,' '); s[i] := ch; (* put char in string *) end; (* if *) until (ord(ch) = eoln_sym); (* if not eoln, get another char *) s := copy(s,1,i); (* correct string length *) writeln (* write a line on the screen *) end; (* read_str *) procedure clear_buf(*unitno:integer*); { modified by SP } begin if unitno=keyport then unitclear( unitno ) end; procedure send_brk; begin cr_break end; function setup_comm{ : boolean}; { SP, 14 Jan 84 } var result: cr_baud_result; begin setup_comm := false; cr_setcommunications(false, false, baud, 8, 1, cr_orig, system_id, result ); case result of CR_bad_parameter : writeln('Bad Parameter, # Bits or Parity wrong'); CR_bad_rate : writeln('Bad Baud Rate selection'); CR_set_OK : setup_comm := true; CR_select_not_supported : writeln('Hardware does not support Baud selection') end {case} end; procedure flush_comm; {added 16 Apr 89, RTC} var ch : char; begin {flush_comm} while CR_remstat do ch := CR_getrem {flush all characters in REMOTE port} end {flush_comm}; function aand(*x,y: integer): integer*); (* arithmetic and--takes 2 integers and ands them, yeilding an integer *) begin aand := ord(odd(x) and odd(y)); (* use as booleans to 'and' them *) end; (* aand *) function aor(*x,y: integer): integer*); (* arithmetic or *) begin aor := ord(odd(x) or odd(y)); (* use as booleans to 'or' them *) end; (* aor *) function xor(*x,y: integer): integer*); (* exclusive or *) begin xor := ord( (odd(x) or odd(y)) and not(odd(x) and odd(y)) ); end; (* xor *) procedure error(*p: packettype; len: integer*); (* writes error message sent by remote host *) var i: integer; begin gotoxy(0,errorline); for i := 0 to len-1 do write(p[i]); gotoxy(0,promptline); end; (* error *) procedure io_error(*i: integer*); var message : string; begin SC_erase_to_EOL( 0, errorline ); {$B OS_ERHDL+} IOR_to_message(i,message); {$E OS_ERHDL+} {$B OS_ERHDL-} case i of 0: message := 'No error'; 1: message := 'Bad Block, Parity error (CRC)'; 2: message := 'Bad Unit Number'; 3: message := 'Bad I/O request, Illegal operation'; 4: message := 'Undefined hardware error'; 5: message := 'Lost unit, Volume is no longer on-line'; 6: message := 'Lost file, File is no longer in directory'; 7: message := 'Bad Title, Illegal file name'; 8: message := 'No room, insufficient space'; 9: message := 'No unit, No such volume on line'; 10: message := 'No file, No such file on volume'; 11: message := 'Duplicate file'; 12: message := 'Not closed, attempt to open an open file'; 13: message := 'Not open, attempt to access a closed file'; 14: message := 'Bad format, error in reading real or integer'; 15: message := 'Queue overflow'; 16: message := 'Write Protected volume'; 17: message := 'Illegal Block'; 18: message := 'Illegal Buffer for low-level I/O'; 19: message := 'Illegal Size or Range of File Attribute'; 20: message := 'Attempted read past End of File'; end; (* case *) if i >= 128 then begin i := i - 128; message := '0'; while i > 0 do begin message[1] := chr(ord('0') + i mod 10); message := concat(' ',message); i := i div 10 end; message := concat('Host Operating System Error #',message) end; {$E OS_ERHDL-} writeln(message); gotoxy(0,promptline) end; (* io_error *) procedure debugwrite(*s: string255*); (* writes a debugging message *) var i: integer; begin if debug then begin SC_erase_to_EOL(0,debugline); gotoxy(0,pred(debugline)); writeln(debf); write(debf,s); for i := 1 to 2000 do ; (* write debugging message *) end (* if debug *) end; (* debugwrite *) procedure debugint(*s: string255; i: integer*); (* write a debugging message and an integer *) begin if debug then begin debugwrite(s); write(debf,i) end (* if debug *) end; (* debugint *) function min(*x,y: integer): integer*); (* returns smaller of two integers *) begin if x < y then min := x else min := y end; (* min *) function tochar(*ch: char): char*); (* tochar converts a control character to a printable one by adding space *) begin tochar := chr(ord(ch) + ord(' ')) end; (* tochar *) function unchar(*ch: char): char*); (* unchar undoes tochar *) begin unchar := chr(ord(ch) - ord(' ')) end; (* unchar *) function ctl(*ch: char): char*); (* ctl toggles control bit: ^A becomes A, A becomes ^A *) begin ctl := chr(xor(ord(ch),64)) end; (* ctl *) procedure echo(*ch: char*); (* echos a character on the screen *) var cursorx, cursory:integer; ch_buf : packed array [0..1] of char; { The DataMedia emulation is by John Socha. } begin ch := chr(aand(ord(ch),127)); (* mask off parity bit *) ch_buf[0] := ch; {for unitwrite portability RTC} if emulating and (ord(ch) in [30,25,28,31,29,11]) then case ord(ch) of { Datamedia 1520 emulation } { rs }30: begin { allow timeout while waiting for coordinates so computer doesn't freeze } set_timer(2); repeat until read_ch( inport, ch ) or timeout; if not timeout then begin cursorx:=ord(ch)-32; repeat until read_ch( inport, ch ) or timeout; if not timeout then begin cursory:=ord(ch)-32; gotoxy(cursorx,cursory) end end end; { em }25: SC_home; { fs }28: SC_right; { us }31: SC_up; { gs }29: SC_erase_to_EOL(SC_find_X,SC_find_Y); { vt }11: SC_eras_eos(SC_find_X,SC_find_Y) end else unitwrite(1,ch_buf[0],1,,12) { the 12 eliminates DLE & CR expansion } end; (* echo *) function getch(*var r: char): boolean*); (* gets a character, strips parity, returns true if it got a char which *) (* isn't Kermit SOH, false if it gets SOH or nothing after timeout *) begin getch := false; repeat until (read_ch(inport,r)) or timeout; (* wait for a character *) if timeout then (* if wait too long then *) exit(getch); (* get out of here *) if parity <> nopar then r := chr(aand(ord(r),127)); (* strip parity from char *) getch := (r <> chr(soh)); (* return true if not SOH *) end; (* getch *) function getsoh(*: boolean*); (* reads characters until it finds an SOH; returns false if has timed out *) var ch: char; begin getsoh := true; repeat repeat until (read_ch(inport,ch)) or timeout; (* wait for a character *) if timeout then begin getsoh := false; exit(getsoh) end; (* if *) ch := chr(aand(ord(ch),127)); (* strip parity of char *) until (ch = chr(SOH)) (* if not SOH, get more *) end; (* getsoh *) function getfil(*filename: string255): boolean*); (* opens a file for writing *) begin (*$I-*) (* turn i/o checking off *) if f_is_binary then begin rewrite(b_file,filename); bufpos := 1 {new file... nothing in buffer} end else rewrite(t_file,filename); (*$I-*) (* turn i/o checking on *) getfil := (ioresult = 0) end; (* getfil *) procedure writescreen(*s: string255*); (* sets up the screen for receiving or sending files *) begin page(output); gotoxy(0,titleline); write(' Kermit UCSD p-System, Version ', version ); gotoxy(statuspos,statusline); write(s); gotoxy(0,packetline); write('Number of Packets: '); gotoxy(0,retryline); write('Number of Tries: '); gotoxy(0,fileline); write('File Name: '); end; (* writescreen *) procedure refresh_screen(*numtry, num: integer*); (* keeps track of packet count on screen *) begin gotoxy(retrypos,retryline); write(numtry: 5); gotoxy(packetpos,packetline); write(num: 5) end; (* refresh_screen *) {$B OS_TIMER+} procedure long_time(var t : time_value); {this procedure converts the "dual integer" values returned by time() to a single "long integer" value, which it returns to the caller} var i : 0..1; hl : array [0..1] of integer; begin {long_time} t := 0; time(hl[0],hl[1]); for i := 0 to 1 do begin if hl[i] < 0 then t := t + 1; t := 65536*t + hl[i] end end {long_time}; {$E OS_TIMER+} procedure set_timer{t : integer}; {added 26 Apr 89, RTC} {$B OS_TIMER-} const counts_per_second = 1000; {WARNING!! implementation dependant} {$E OS_TIMER-} var long_t : time_value; begin {set_timer} long_t := t; {convert to long format} {$B OS_TIMER+} long_time(time_limit); time_limit := time_limit + 60*long_t {$E OS_TIMER+} {$B OS_TIMER-} time_limit := counts_per_second*long_t {$E OS_TIMER-} end {set_timer}; function timeout {: boolean}; {added 26 Apr 89, RTC} {$B OS_TIMER+} var this_time : time_value; {$E OS_TIMER+} begin {timeout} {$B OS_TIMER+} long_time(this_time); timeout := this_time > time_limit {$E OS_TIMER+} {$B OS_TIMER-} time_limit := time_limit - 1; timeout := time_limit <= 0 {$E OS_TIMER-} end {timeout}; procedure utl_version; begin write(my_version); {$B OS_TIMER+} write(' (with TIMER)'); {$E OS_TIMER+} writeln end {utl_version}; begin { body of unit kermutil } { initialization code } old_flush := syscom^.crtinfo.flush; old_stop := syscom^.crtinfo.stop; syscom^.crtinfo.flush := chr(255); { effectively turning flush off } syscom^.crtinfo.stop := chr(254); { effectively turning stop off } ***; { termination code } syscom^.crtinfo.flush := old_flush; { turn flush back on } syscom^.crtinfo.stop := old_stop { turn stop back on } end. { kermutil } {>>>> SETSHOW.TEXT} { Change log: 30 Apr 89, V1.1: moved into kermutil RTC 30 Apr 89, V1.1: Added SET INTERFACE command RTC 16 Apr 89, V1.1: Added Client Unit to SHOW VER command RTC 14 Apr 89, V1.1: Added SHOW VERSION command RTC 14 Aug 88: Added SYSTEM-ID and modified DEBUG RTC 31 Jul 88: Modified to permit REMUNIT to accept/reject baud rate RTC } procedure fill_parity_array; (* parity value table for even parity...not(entry) = odd parity *) const min = 0; max = 255; var i, shifter, counter: integer; ch: char; begin for ch := chr(min) to chr(max) do case parity of evenpar: begin shifter := aand(ord(ch),255); (* mask off parity bit *) counter := 0; for i := 1 to 7 do begin (* count the 1's *) if odd(shifter) then counter := counter + 1; shifter := shifter div 2 end; (* for i *) if odd(counter) then (* stick a 1 on if necessary *) parity_array[ch] := chr(aor(ord(ch),128)) else parity_array[ch] := chr(aand(ord(ch),127)) end; (* for ch *) (* case even *) oddpar: begin shifter := aand(ord(ch),255); (* mask off parity bit *) counter := 0; for i := 1 to 7 do begin (* count the 1's *) if odd(shifter) then counter := counter + 1; shifter := shifter div 2 end; (* for i *) if odd(counter) then (* stick a 1 on if necessary *) parity_array[ch] := chr(aand(ord(ch),127)) else parity_array[ch] := chr(aor(ord(ch),128)) end; (* for ch *) (* case odd *) markpar: parity_array[ch] := chr(aor(ord(ch),128)); spacepar:parity_array[ch] := chr(aand(ord(ch),127)); nopar: parity_array[ch] := ch; end; (* case *) end; (* fill_parity_array *) procedure write_bool{s: string255; b: boolean}; (* writes message & 'on' if b, 'off' if not b *) begin write(s); case b of true: writeln('on'); false: writeln('off'); end; (* case *) end; (* write_bool *) procedure show_parms; (* shows the various settable parameters *) var i,first,last : vocab; begin if noun = allsym then begin first := baudsym; last := systemsym end else begin first := noun; last := noun end; for i := first to last do case i of debugsym: write_bool('Debugging is ',debug); escsym: writeln('Escape character is ^',ctl(esc_char)); filenamsym: begin write('File names are '); if lit_names then write('Literal') else write('Converted'); writeln end; filetypesym: begin write('File type is '); if f_is_binary then write('Binary') else write('Text'); writeln end; filewarnsym: write_bool('File warning is ',fwarn); ibmsym: write_bool('IBM is ',ibm); localsym: write_bool('Local echo is ',halfduplex); emulatesym: write_bool('Emulate DataMedia is ', emulating ); baudsym: writeln( 'Baud rate is ', baud:5 ); paritysym: begin case parity of evenpar: write('Even'); markpar: write('Mark'); nopar: write('No'); oddpar: write('Odd'); spacepar: write('Space'); end; (* case *) writeln(' parity'); end; (* paritysym *) systemsym: writeln('System ID is ',system_id); end; (* case *) if noun = versionsym then begin writeln(ker_version); rec_version; sen_version; cli_version; hlp_version; pak_version; utl_version; gbl_version; mnu_version; par_version; end end; (* show_sym *) procedure set_parms; (* sets the parameters *) var oldbaud : integer; begin case noun of debugsym: debug := adj = onsym; escsym: escchar := newescchar; filenamsym : lit_names := adj = litsym; filetypesym : f_is_binary := adj = binsym; filewarnsym: fwarn := (adj = onsym); ibmsym: case adj of onsym: begin ibm := true; parity := markpar; half_duplex := true; fillparityarray end; (* onsym *) offsym: begin ibm := false; parity := nopar; half_duplex := false; fillparityarray end; (* onsym *) end; (* case adj *) intsym: if adj = ucsdsym then menu_interface; localsym: halfduplex := (adj = onsym); emulatesym: emulating := (adj = onsym); paritysym: begin case adj of evensym: parity := evenpar; marksym: parity := markpar; nonesym: parity := nopar; oddsym: parity := oddpar; spacesym: parity := spacepar; end; (* case *) fill_parity_array; end; (* paritysym *) baudsym: begin oldbaud := baud; baud := newbaud; if not setup_comm then baud := oldbaud end { baudsym }; systemsym: system_id := line; end; (* case *) end; (* set_parms *) {>>>> KERMENUS.TEXT} unit kermenus; interface {Change log: 14 May 89, V1.1: Added Parameters menu RTC 02 May 89, V1.1: Added menu to control log files RTC 30 Apr 89, V1.1: Originally written RTC } procedure menu_interface; procedure mnu_version; implementation uses screenops, {$U kermglob.code} kermglob, {$U kermutil.code} kermutil, {$U sender.code} sender, {$U receiver.code} receiver, {$U client.code} client; const my_version = ' Kermenus Unit V1.1, 14 May 89'; procedure transfer_files; var ch : char; begin {transfer_files} ch := SC_prompt(concat('Kermit-UCSD File Transfer: ', 'S(end, R(eceive, G(et, P(ut, A(bort'), -1,-1,0,menu_line, ['S','R','G','P','A',' '], false,','); SC_clr_line(menu_line); case ch of 'G', 'R' : begin if ch = 'G' then begin gotoxy(file_pos,file_line); readln(xfilename); uppercase(xfilename) end; recsw(rec_ok,ch = 'G'); gotoxy(0,debugline); write(chr(bell)); if rec_ok then writeln('successful receive') else writeln('unsuccessful receive'); (*$I-*) (* set i/o checking off *) if f_is_binary then close(b_file) else close(t_file); (*$I+*) (* set i/o checking back on *) end; (* recsym *) 'P', 'S' : begin gotoxy(file_pos,file_line); readln(xfilename); uppercase(xfilename); sendsw(send_ok); gotoxy(0,debugline); write(chr(bell)); if send_ok then writeln('successful send') else writeln('unsuccessful send'); (*$I-*) (* set i/o checking off *) if f_is_binary then close(b_file) else close(t_file); (*$I+*) (* set i/o checking back on *) end; (* sendsym *) 'A', ' ' : begin gotoxy(0,debugline); write('file transfer aborted'); end; {abort transfer} end {case ch} end {transfer_files}; procedure logs; var ch_cmd,ch_log : char; log_message : string; begin {logs} ch_cmd := SC_prompt(concat('Kermit-UCSD Logs: ', 'O(pen, C(lose, A(bort'), -1,-1,0,menu_line, ['O','C','A',' '], false,','); case ch_cmd of 'O' : log_message := 'Open'; 'C' : log_message := 'Close'; 'A',' ' : exit(logs) end {case ch_cmd}; ch_log := SC_prompt(concat('Kermit-UCSD ',log_message,' Log: ', 'D(ebug, A(bort'), -1,-1,0,menu_line, ['D','A',' '], false,','); case ch_log of 'D' : log_message := concat(log_message,' for Debug'); 'A',' ' : exit(logs) end {case ch_log}; if ch_cmd = 'O' then {command was to open log} begin SC_clr_line(menu_line); write('File to ',log_message,' Logging>'); readln(xfilename); uppercase(xfilename); {$I-} case ch_log of 'D' : begin close(debf,lock); rewrite(debf,xfilename) end; end {case ch_log}; if ioresult <> 0 then begin writeln('Unable to open ',xfilename); case ch_log of 'D' : begin close(debf); rewrite(debf,'CONSOLE:') end; end {case ch_log}; end else {$I+} case ch_log of 'D' : write(debf, ker_version,' -- Debug log...'); end end else {command was to close log} begin {$I-} case ch_log of 'D' : close(debf,lock); end {case ch_log}; if ioresult <> 0 then begin writeln('Unable to close file'); end; case ch_log of 'D' : rewrite(debf,'CONSOLE:'); end {case ch_log}; {$I+} end; end {logs}; procedure menu_interface; var done : boolean; ch : char; procedure write_bool(b: boolean); {writes 'True' or 'False'} begin {write_bool} if b then write('True ') else write('False') end {write_bool}; procedure read_bool(var b: boolean); var ch : char; begin {read_bool} SC_getc_ch(ch,['T','F']); b := ch = 'T' end {read_bool}; procedure parameters; const name_line = 9; type_line = 10; warn_line = 11; baud_line = 12; parity_line = 13; echo_line = 14; ibm_line = 15; em_line = 16; esc_line = 17; debug_line = 18; sys_line = 19; opt_pos = 4; val_pos = 25; begin {parameters} SC_eras_eos(0,pred(name_line)); repeat gotoxy(opt_pos,name_line); write('File N(ames'); gotoxy(val_pos,name_line); if lit_names then write('Literal ') else write('Converted'); gotoxy(opt_pos,type_line); write('File T(ype'); gotoxy(val_pos,type_line); if f_is_binary then write('Binary') else write('Text '); gotoxy(opt_pos,warn_line); write('File W(arning'); gotoxy(val_pos,warn_line); write_bool(f_warn); gotoxy(opt_pos,baud_line); write('B(aud rate'); gotoxy(val_pos,baud_line); write(baud); gotoxy(opt_pos,parity_line); write('P(arity'); gotoxy(val_pos,parity_line); case parity of evenpar: write('Even'); markpar: write('Mark'); nopar: write('None'); oddpar: write('Odd'); spacepar: write('Space'); end {case parity}; gotoxy(opt_pos,echo_line); write('L(ocal echo'); gotoxy(val_pos,echo_line); write_bool(half_duplex); gotoxy(opt_pos,ibm_line); write('I(BM mode'); gotoxy(val_pos,ibm_line); write_bool(ibm); gotoxy(opt_pos,em_line); write('eM(ulate Datamedia'); gotoxy(val_pos,em_line); write_bool(emulating); gotoxy(opt_pos,esc_line); write('E(scape Character'); gotoxy(val_pos,esc_line); write('^',ctl(esc_char)); gotoxy(opt_pos,debug_line); write('D(ebugging'); gotoxy(val_pos,debug_line); write_bool(debug); gotoxy(opt_pos,sys_line); write('S(ystem ID'); gotoxy(val_pos,sys_line); write(system_id); ch := SC_prompt(concat('Kermit Parameters: {options} ', ' to leave, ', 'switch to K(ermit style interface, V(ersion'), -1,-1,0,menu_line, ['D','E','N','T','W','I','L','M','B','P','S','K','V',' '], false,','); case ch of 'D' : begin SC_erase_to_EOL(val_pos,debug_line); read_bool(debug) end; 'E' : repeat SC_erase_to_EOL(val_pos,esc_line); read(keyboard,esc_char) until esc_char in [chr(0)..chr(31)]; 'N' : begin SC_erase_to_EOL(val_pos,name_line); SC_getc_ch(ch,['L','C']); lit_names := ch = 'L' end; 'T' : begin SC_erase_to_EOL(val_pos,type_line); SC_getc_ch(ch,['B','T']); f_is_binary := ch = 'B' end; 'W' : begin SC_erase_to_EOL(val_pos,warn_line); read_bool(f_warn) end; 'I' : begin SC_erase_to_EOL(val_pos,ibm_line); read_bool(ibm); if ibm then begin parity := markpar; half_duplex := true end else begin parity := nopar; half_duplex := false end; fill_parity_array end; 'L' : begin SC_erase_to_EOL(val_pos,echo_line); read_bool(halfduplex) end; 'M' : begin SC_erase_to_EOL(val_pos,em_line); read_bool(emulating) end; 'B' : repeat SC_erase_to_EOL(val_pos,baud_line); {$I-} read(baud); {$I+} SC_erase_to_EOL(0,menu_line) until setup_comm; 'P' : begin SC_erase_to_EOL(val_pos,parity_line); SC_getc_ch(ch,['E','O','M','S','N']); case ch of 'E' : parity := evenpar; 'M' : parity := markpar; 'N' : parity := nopar; 'O' : parity := oddpar; 'S' : parity := spacepar; end {case ch}; fill_parity_array end; 'S' : begin SC_erase_to_EOL(val_pos,sys_line); readln(system_id) end; 'K' : begin done := true; {switch back to KERMIT style interface} SC_clr_screen; exit(parameters) end; 'V' : begin SC_eras_eos(0,name_line); noun := versionsym; show_parms; exit(parameters) end; ' ' : exit(parameters); end {case ch} until false end {parameters}; begin {menu_interface} done := false; writescreen(''); repeat ch := SC_prompt(concat('Kermit-UCSD: ', 'C(onnect, T(ransfer Files, Q(uit, ', 'S(et Parameters, L(ogs, B(ye, F(inish'), -1,-1,0,menu_line, ['C','T','Q','S','L','B','F'], false,','); SC_clr_line(status_line); SC_clr_line(debug_line); case ch of 'C' : begin SC_clr_screen; connect; writescreen('') end; 'T' : transfer_files; 'L' : logs; 'F', 'B' : begin case ch of 'F' : line := 'F'; 'B' : line := 'L'; end {case}; clientsw(send_ok,'G',line); gotoxy(0,debugline); write(chr(bell)); if send_ok then writeln('successful transaction') else writeln('unsuccessful transaction'); (*$I-*) (* set i/o checking off *) close(t_file); (*$I+*) (* set i/o checking back on *) end; {generic server command} 'S' : parameters; 'Q' : begin done := true; verb := quitsym end; end {case ch} until done end {menu_interface}; procedure mnu_version; begin {mnu_version} writeln(my_version) end {mnu_version}; end {kermenus}. {>>>> KERMPACK.TEXT} unit kermpack; interface uses {$U kermglob.code} kermglob; {Change log: 30 Apr 89, V1.1: Eliminated "no timeout on receive" checks RTC 26 Apr 89, V1.1: Changed to "timer" controlled timeouts RTC 19 Apr 89, V1.1: minor cleanups RTC 13 Apr 89, V1.1: Added Version message RTC 14 Aug 88: Fixed packetwrite to output to debf RTC 31 Jul 88: Modified for exact size binary xfr, misc. cleanup RTC 02 Jul 88: Added binary transfers RTC } procedure spar(var packet: packettype); procedure rpar(var packet: packettype; len : integer); procedure spack(ptype: char; num:integer; len: integer; data: packettype); function rpack(var len, num: integer; var data: packettype): char; procedure bufemp(buffer: packettype; len: integer); function bufill(var buffer: packettype): integer; procedure pak_version; implementation uses {$U kermutil.code} kermutil; const my_version = ' Kermpack Unit V1.1, 30 Apr 89'; procedure bufemp(*buffer: packettype; var f: text; len: integer*); (* empties a packet into a file *) { Note: this strips out ALL linefeed characters! } var i,ls: integer; r: char; set_bit_8 : boolean; s: string255; procedure write_bin; var dummy : integer; begin {write_bin} filebuf[bufpos] := r; i := succ(i); bufpos := succ(bufpos); if bufpos > blksize then begin {$I-} dummy := blockwrite(b_file,filebuf,1); if io_result <> 0 then begin io_error(ioresult); {tell them and...} currstate := 'a' {abort} end; {$I+} bufpos := 1 end end {write_bin}; procedure write_text; var dummy : integer; begin {write_text} if ord(r) = lf then { skip linefeeds SP } i := i + 1 else if (ord(r) = cr) then begin (* else if a carriage return then *) i := i + 1; (*$I-*) (* turn i/o checking off *) writeln(t_file,s); (* and write out line to file *) s := copy('',0,0); (* empty the string var *) ls := 0; (*$I+*) (* turn i/o checking back on *) end else begin (* else, is a regular char, so Q5R $H s := concat(s,' '); (* and add character to out string *) ls := ls + 1; s[ls] := r; if length(s) >= 255 then {dump full string RTC} begin {$I-} write(t_file,s); s := ''; ls := 0 {$I+} end; i := i + 1 (* increase buffer pointer *) end; (* else *) if (io_result <> 0) then begin (* if io_error *) io_error(ioresult); (* tell them and *) currstate := 'a'; (* abort *) end (* if *) end {write_text}; begin s := copy('',0,0); ls := 0; i := 0; while i < len do begin r := buffer[i]; (* get a character *) if en_qbin and (r = qbin) then begin i := succ(i); r := buffer[i]; {get 8 bit quoted char} set_bit_8 := true end else set_bit_8 := false; if (r = myquote) then begin (* if character is control quote *) i := i + 1; (* skip over quote and *) r := buffer[i]; (* get quoted character *) if not (chr(aand(ord(r),127)) in ctl_set - [chr(0)..chr(31),chr(del)]) then r := ctl(r); (* controllify it *) end; (* if *) if set_bit_8 then r := chr(aor(ord(r),128)); if f_is_binary then write_bin else write_text end; (* while *) (* and get another char *) if not f_is_binary then begin (*$I-*) (* turn i/o checking off *) write(t_file,s); (* and write out line to file *) if (io_result <> 0) then begin (* if io_error *) io_error(ioresult); (* tell them and *) currstate := 'a'; (* abort *) end (* if *) (*$I+*) (* turn i/o checking back on *) end end; (* bufemp *) function bufill(*var buffer: packettype): integer*); (* fill a packet with data from a file *) var i : integer; r : char; function done : boolean; begin {done} if f_is_binary then done := (bufpos > last_blksize) and eof(b_file) else done := eof(t_file) end {done}; begin i := 0; (* while file has some data & packet has some room we'll keep going *) while not done and (i < spsiz-9) do begin if f_is_binary then begin (* if we need more data from disk then *) if (bufpos > bufend) and (not eof(b_file)) then begin {$I-} bufend := blockread(b_file,filebuf[1],1) * blksize; if io_result <> 0 then begin bufill := at_badblk; exit(bufill) end; {$I+} (* and adjust buffer pointer *) bufpos := 1 end; (* if *) r := filebuf[bufpos]; (* get a character *) bufpos := bufpos + 1; (* increase buffer pointer *) end else begin r := t_file^; {$I-} if eoln(t_file) then begin buffer[i] := quote; (* put (quoted) CR in buffer *) i := i + 1; buffer[i] := ctl(chr(cr)); i := i + 1; r := chr(lf); (* and we'll stick a LF after *) end; get(t_file); if io_result <> 0 then begin bufill := at_badblk; exit(bufill) end {$I+} end; if en_qbin and (ord(r) > 127) then begin r := chr(ord(r)-128); {remove the 8th bit} buffer[i] := qbin; {insert prefix} i := succ(i) end; if chr(aand(ord(r),127)) in ctl_set then (* if a control char *) begin buffer[i] := quote; (* put the quote in buffer *) i := i + 1; if not (chr(aand(ord(r),127)) in ctl_set - [chr(0)..chr(31),chr(del)]) then r := ctl(r); (* and un-controllify char *) end (* if *); buffer[i] := r; i := i + 1; end; (* while *) if (i = 0) then (* if we're at end of file, *) bufill := at_eof (* indicate it *) else (* else *) bufill := i (* return # of chars in packet *) end; (* bufill *) procedure spar(*var packet: packettype*); (* fills data array with my send-init parameters *) begin packet[0] := tochar(chr(maxpack+1)); (* biggest packet i can receive *) packet[1] := tochar(chr(mytime)); (* when i want to be timed out *) packet[2] := tochar(chr(mypad)); (* how much padding i need *) packet[3] := ctl(chr(mypchar)); (* padding char i want *) packet[4] := tochar(chr(myeol)); (* end of line character i want *) packet[5] := myquote; (* control-quote char i want *) if parity = nopar then packet[6] := 'Y' (* I will do 8-bit quoting *) else packet[6] := my_qbin; { I need to do 8-bit quoting } packet[7] := '1'; { checksum type I want } packet[8] := 'N'; { I will not do run len encoding } packet[9] := tochar(chr(8)); { I can do attributes packets } debugwrite('spar:') end; (* spar *) procedure rpar(*var packet: packettype; len : integer*); (* gets their init params *) begin if len > 0 then spsiz := ord(unchar(packet[0])) (* max send packet size *) else spsiz := 80; if len > 1 then timint := ord(unchar(packet[1])) (* when i should time out *) else timint := my_time; if len > 2 then pad := ord(unchar(packet[2])) (* number of pads to send *) else pad := 0; if len > 3 then padchar := ctl(packet[3]) (* padding char to send *) else padchar := chr(my_pchar); if len > 4 then xeol := unchar(packet[4]) (* eol char i must send *) else xeol := chr(my_eol); if len > 5 then quote := packet[5] (* incoming data quote char *) else quote := my_quote; if len > 6 then qbin := packet[6] { incoming 8th bit quote } else qbin := 'N'; if parity = nopar then en_qbin := qbin in [chr(33)..chr(62),chr(96)..chr(126)] else begin if q_bin = 'Y' then qbin := my_qbin; en_qbin := qbin = my_qbin end; if len > 9 then en_attr := aand(ord(unchar(packet[9])),8) = 8 else en_attr := false; debugwrite('rpar:') end; (* rpar *) procedure packetwrite(p: packettype; len: integer); (* writes out all of a packet for debugging purposes *) var i: integer; begin gotoxy(0,debugline); for i := 0 to len-1 do write(debf,p[i]) end; (* packetwrite *) procedure spack(*ptype: char; num: integer; len: integer; data: packettype*); (* send a packet *) var i: integer; chksum: char; ch: char; begin debugwrite('spack:'); if ibm and (currstate <> 's') then (* if ibm and not SINIT then *) begin set_timer(timint); repeat (* wait for an xon *) repeat until (readch(inport, ch)) or timeout; until (ch = xon) or timeout; if timeout then (* if wait too long then *) begin exit(spack) (* get out *) end; (* if *) end; (* if *) for i := 1 to pad do write_ch(oport,parity_array[padchar]);(* write out any padding chars *) write_ch(oport,parity_array[chr(soh)]); (* packet sync character *) chksum := tochar(chr(len + 3)); (* init chksum *) write_ch(oport,parity_array[tochar(chr(len + 3))]); (* character count *) chksum := chr(ord(chksum) + ord(tochar(chr(num)))); write_ch(oport,parity_array[tochar(chr(num))]); chksum := chr(ord(chksum) + ord(ptype)); write_ch(oport,parity_array[ptype]); (* packet type *) for i := 0 to len - 1 do (* loop through data chars *) begin write_ch(oport,parity_array[data[i]]); (* store char *) chksum := chr(ord(chksum) + ord(data[i])) end; (* for i *) (* compute final chksum *) chksum := chr(aand(ord(chksum) + (aand(ord(chksum),192) div 64), 63)); write_ch(oport,parity_array[tochar(chksum)]); write_ch(oport,parity_array[xeol]); if debug then begin write(debf,' len:',len,' num:',num,' ptype:',ptype); packetwrite(data,len); write(debf,' chksum:',tochar(chksum)) end end; (* spack *) (*$G+*) (* turn on goto option...need it for next routine *) function rpack(*var len, num: integer; var data: packettype): char*); (* read a packet *) label 1; (* used to emulate C's CONTINUE statement *) var i, ichksum: integer; chksum, ptype: char; r: char; begin debugwrite('rpack:'); set_timer(timint); if not getsoh then (*if don't get synch char then *) begin rpack := 'N'; (* treat as a NAK *) num := n mod 64; exit(rpack) (* and get out of here *) end; 1: if timeout then (* if we've tried too many times *) begin (* and aren't waiting for init *) rpack := 'N'; (* treat as NAK *) exit(rpack) (* and get out of here *) end; (* if *) if not getch(r) then (* get a char and *) goto 1; (* resynch if soh *) ichksum := ord(r); (* start checksum *) len := ord(unchar(r)) - 3; (* character count *) if not getch(r) then (* get a char and *) goto 1; (* resynch if soh *) ichksum := ichksum + ord(r); num := ord(unchar(r)); (* packet number *) if not getch(r) then (* get a char and *) goto 1; (* resynch if soh *) ichksum := ichksum + ord(r); ptype := r; (* packet type *) for i := 0 to len-1 do (* get any data *) begin if not getch(r) then (* get a char and *) goto 1; (* resynch if soh *) ichksum := ichksum + ord(r); data[i] := r; end; (* for i *) data[len] := chr(0); (* mark end of data *) if not getch(r) then (* get a char and *) goto 1; (* resynch if soh *) (* compute final checksum *) chksum := chr(aand(ichksum + (aand(ichksum,192) div 64), 63)); if (chksum <> unchar(r)) then (* if checksum bad *) rpack := chr(0) (* return 'false' indicator *) else (* else *) rpack := ptype; (* return packet type *) if debug then begin write(debf,' len:',len,' num:',num,' ptype:',ptype); packetwrite(data,len); write(debf,' chksum:',r) end; (* if *) end; (* rpack *) (*$G-*) (* turn off goto option...don't need it anymore *) procedure pak_version; begin writeln(my_version) end {pak_version}; end. { kermpack } {>>>> KERMGLOB.TEXT} unit kermglob; interface {Change log: 13 May 89, V1.1: Added COMMENT vocab. & Eliminated "int_bool_rec" RTC 30 Apr 89, V1.1: Added vocabulary for SET INTERFACE command RTC 26 Apr 89, V1.1: minor cleanups RTC 16 Apr 89, V1.1: Added BYE & FINISH commands RTC 13 Apr 89, V1.1: Added Version message RTC 14 Aug 88: Added LOG, CLOSE, and SET SYSTEM commands RTC 31 Jul 88: Added variable system_id string for REMUNIT RTC 31 Jul 88: Added attributes packets & exact size bin. xfrs RTC 10 Jul 88: Removed screen command definitions RTC 30 Jun 88: Modified for binary files, "take", ^X & ^Z RTC } const blksize = 512; oport = 8; (* output port # *) inport = 7; keyport = 2; bell = 7; (* ASCII bell *) maxpack = 93; (* maximum packet size minus 1 *) soh = 1; (* start of header *) sp = 32; (* ASCII space *) cr = 13; (* ASCII CR *) lf = 10; (* ASCII line feed *) del = 127; (* delete *) can_cur = 24; { cancel current file char ^X } can_all = 26; { cancel all files char ^Z } my_esc = 29; (* default esc char for connect (^]) *) maxtry = 5; (* number of times to retry sending packet *) my_quote = '#'; (* quote character I'll use *) my_qbin = '&'; { 8th bit quote character I want } my_pad = 0; (* number of padding chars I need *) my_pchar = 0; (* padding character I need *) my_eol = 13; (* end of line character i need *) my_time = 5; (* seconds after which I should be timed out *) maxtim = 20; (* maximum timeout interval *) mintim = 2; (* minimum time out interval *) at_eof = -1; (* value to return if at eof *) at_badblk = -2; { value to return if at bad block } {rqsize = 5000; (* input queue size *) qsize1 = 5001; (* qsize + 1 *)} eoln_sym = 13; (* pascal eoln sym *) back_space = 8; (* pascal backspace sym *) defaultbaud = 1200; (* default baud rate *) (* screen control information *) (* console line on which to put specified info *) menu_line = 0; title_line = 2; statusline = 3; packet_line = 4; retry_line = 5; file_line = 6; error_line = 7; debug_line = 8; prompt_line = 9; (* position on line to put info *) statuspos = 60; packet_pos = 19; retry_pos = 17; file_pos = 11; type packettype = packed array[0..maxpack] of char; parity_type = (evenpar, oddpar, markpar, spacepar, nopar); string255 = string[255]; statustype = (null, at_eol, unconfirmed, parm_expected, ambiguous, unrec, fn_expected, ch_expected, num_expected); vocab = (nullsym, allsym, baudsym, binsym, byesym, closesym, comsym, consym, convsym, debugsym, emulatesym, escsym, evensym, exitsym, filenamsym, filetypesym, filewarnsym, finsym, getsym, helpsym, ibmsym, intsym, kermitsym, litsym, localsym, logsym, marksym, nonesym, oddsym, offsym, onsym, paritysym, putsym, quitsym, recsym, sendsym, setsym, showsym, spacesym, systemsym, takesym, textsym, ucsdsym, versionsym); var noun, verb, adj: vocab; status: statustype; vocablist: array[vocab] of string[13]; xfilename, line: string255; newescchar: char; expected: set of vocab; newbaud: integer; currstate: char; (* current state *) xeol, quote, qbin, esc_char: char; lit_names, f_is_binary, fwarn, ibm, half_duplex, en_attr, en_qbin, debug: boolean; i, size, rpsiz, spsiz, pad, n, num_try, oldtry, timint: integer; recpkt, packet: packettype; padchar, ch: char; s: string255; debf: text; (* file for debug output *) parity: parity_type; xon: char; filebuf: packed array[1..blksize] of char; bufpos, bufend: integer; parity_array: packed array[char] of char; ctlset: set of char; rec_ok, send_ok: boolean; baud: integer; emulating: boolean; last_blksize : integer; {size of last block of boolean file} t_file : text {file for text file transfers}; b_file : file {file for binary file transfers}; cmd_file : text {file of "take" commands}; ker_version, { version id for other units } system_id : string {id string for REMUNIT}; procedure gbl_version; implementation const my_version = ' Kermglob Unit V1.1, 13 May 89'; procedure gbl_version; begin writeln(my_version) end {gbl_version}; end. { kermglob } {>>>> UCPECAN.M.TEXT} ckermglob cfakeutil kermutil ckermpack cparser chelper csender creceiver cclient ckermenus ckermutil ckermit {>>>>}