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