home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Columbia Kermit
/
kermit.zip
/
archives
/
mtspascal.tar.gz
/
mtspascal.tar
/
mtsker.pas
< prev
next >
Wrap
Pascal/Delphi Source File
|
1984-01-06
|
40KB
|
1,121 lines
(* 12/14/83 - Time out on first packet added *)
(* 12/14/83 - MTS system calls silenced *)
(* 12/05/83 - Carriage control option implemented *)
(* 12/03/83 - Tape mode and IBM mode established *)
(* 11/21/83 - Program commented *)
(* 11/19/83 - History line begun *)
(* 11/16/83 - complete working version in place *)
PROGRAM kermit;
(*
KERMIT file transfer utility for the Michigan Terminal System (MTS).
Version 1.0 written by William S. Hall, Mathematical Reviews,
Ann Arbor, MI in PASCAL/VS.
For program usage and limitations see SJ1K:kermit.doc
*)
%page
CONST
(*
Ordinal values of control characters. Where values differ between
the EBCDEC and ASCII control characters, then are so noted.
*)
NUL = 00; SOH = 01; STX = 2; ETX = 03;
EOT = 55; (* A/E = 04/55 *)
ENQ = 45; (* A/E = 05/45 *)
ACK = 46; (* A/E = 06/46 *)
BEL = 47; (* A/E = 07/47 *)
BS = 22; (* A/E = 08/22 *)
HT = 05; (* A/E = 09/05 *)
LF = 37; (* A/E = 10/37 *)
VT = 11; FF = 12; CR = 13; SO = 14;
SI = 15; DLE = 16; DC1 = 17; DC2 = 18;
DC3 = 19;
DC4 = 60; (* A/E = 20/60 *)
NAK = 61; (* A/E = 21/61 *)
SYN = 50; (* A/E = 22/50 *)
ETB = 38; (* A/E = 23/38 *)
CAN = 24;
EM = 25;
SUB = 63; (* A/E = 26/63 *)
ESC = 39; (* A/E = 27/39 *)
FS = 28;
GS = 29;
RS = 30;
US = 31;
SP = 64; (* A/E = 32/64 *)
DEL = 7; (* A/E = 127/7 *)
(* Other program constants needed in the program *)
MAXPACK = 94; (* Maximum packet size *)
MAXTRY = 5; (* Times to retry a packet *)
MYQUOTE = '#'; (* Quote character I will use *)
MYPAD = 0; (* Number of padding characters I need *)
MYPCHAR = NUL; (* Ordinal value of padding character I need *)
MYEOL = CR; (* Ordinal value of end of line char I need *)
MYTIME = 5; (* Seconds after which I should be timed out *)
NAMESIZE = 40; (* Maximum size of file name *)
MAXFILES = 20; (* Maximum number of files to send *)
SNDINIT_DLY = 8000000; (* Delay in microseconds before first packet *)
%page
TYPE
(* These types are used to call MTS procedures *)
char255 = packed array[1..255] of char;
halfword = packed -32768..32767;
(* This type holds a packet being received or sent *)
packet_type = packed array[1..MAXPACK] of char;
(* This points to a packet *)
packet_ptr = @packet_type;
(* Timeout variable for system time-out call *)
intpair = array[1..2] of integer;
VAR
date : alfa; (* used for running date and time call *)
time : alfa;
delay : intpair; (* used for calling twait procedure *)
cc : boolean; (* Carriage control char in column 1? *)
ccinfo : char; (* used to set value of cc from input *)
col : integer; (* Marks column position *)
cmdstr : char255; (* used to issue commands to MTS *)
ascii : boolean; (* ascii char set in use *)
i : integer; (* Utility integer *)
size : integer; (* Size of present data *)
n : integer; (* Message number *)
rpsiz : integer; (* Maximum receive packet size *)
spsiz : integer; (* Maximum send packet size *)
pad : integer; (* How much padding to send *)
timint : integer; (* Timeout for foreign host on sends *)
numtry : integer; (* Times this packet tried *)
oldtry : integer; (* Times previous packet retried *)
debug : boolean; (* true means debugging *)
state : char; (* Present state of the automaton *)
padchar : char; (* Padding character to send *)
eol : char; (* End of line character to send *)
quote : char; (* Quote character in incoming data *)
recpkt : packet_ptr; (* Receive packet buffer pointer *)
packet : packet_ptr; (* Send packet buffer pointer *)
command : char; (* Command - receive or send *)
filnam : array[1..MAXFILES] of string(NAMESIZE); (* holds file names *)
nfiles : integer; (* number of files to send *)
numsent : integer; (* number already send *)
bugfil : text; (* debug file *)
sndfil : text; (* file to be sent *)
rcvfil : text; (* file to be received *)
%page
PROCEDURE cmdnoe(const cmd : char255; const len : halfword); fortran;
(* Makes MTS calls *)
PROCEDURE twait(const code : integer; const val : intpair); fortran;
(* Executes delays *)
PROCEDURE setsys;
(*
Set the terminal for file transfer so that no packets are wrapped
and the terminal is not paged. Also MTS must not echo characters
during the transfer, and control characters, especially control A,
must be allowed to pass unintercepted by the front end (Hermes).
Finally, reader mode allows XON-XOFF flow control.
*)
BEGIN
cmdnoe('$control *msink* width=255', 26);
cmdnoe('$control *msink* outlen=255', 27);
cmdnoe('$control *msink* reader=on', 26);
cmdnoe('$control *msink* echo=off', 25);
cmdnoe('$control *msink* npc=off', 24);
cmdnoe('$control *msink* pagewait=off', 29);
END; {setsys}
PROCEDURE resetsys;
(* Restore the user's system after completion of run *)
BEGIN
cmdnoe('$control *msink* reset', 22);
END; {resetsys}
FUNCTION toupper(c : char) : char;
(* Convert lower to upper case *)
BEGIN
if ((c >= 'a') and (c <= 'i')) or ((c >= 'j') and (c <= 'r'))
or ((c >= 's') and (c <= 'z')) then
BEGIN
if ascii
then toupper := chr(ord(c) - 32)
else
toupper := chr(ord(c) + 64)
END
else toupper := c;
END; {toupper}
FUNCTION checksum(c : INTEGER) : INTEGER; (* checksum based on ASCII sum *)
(*
Compute a checksum in the range 0 to 63. This is a Pascal version
of the formula (sum + (sum & 192) div 64) & 63, where & is bitwise 'and'
*)
VAR
x : INTEGER;
BEGIN
x := (c MOD 256) DIV 64;
x := x + c;
checksum := x MOD 64;
END; {checksum}
%page
FUNCTION tochar(ch : integer) : char;
(*
Converts an integer in the range 0 to 94 to a printing character.
If ASCII is the underlying character set, this is trivial. For
EBCDEC, the internal representation of characters in Pascal/VS,
a case statement is appropriate. Note that three characters,
namely, "^", "`", and "\" cannot be represented in quotes and
chr(ordinal value) is used instead. This seems to be a pecularity
of the MTS operating system and not EBCDEC in general.
*)
BEGIN
if ascii then
tochar := chr(ch + 32)
else case ch of
0 : tochar := ' '; 1 : tochar := '!'; 2 : tochar := '"';
3 : tochar := '#'; 4 : tochar := '$'; 5 : tochar := '%';
6 : tochar := '&'; 7 : tochar := ''''; 8 : tochar := '(';
9 : tochar := ')'; 10 : tochar := '*'; 11 : tochar := '+';
12 : tochar := ','; 13 : tochar := '-'; 14 : tochar := '.';
15 : tochar := '/'; 16 : tochar := '0'; 17 : tochar := '1';
18 : tochar := '2'; 19 : tochar := '3'; 20 : tochar := '4';
21 : tochar := '5'; 22 : tochar := '6'; 23 : tochar := '7';
24 : tochar := '8'; 25 : tochar := '9'; 26 : tochar := ':';
27 : tochar := ';'; 28 : tochar := '<'; 29 : tochar := '=';
30 : tochar := '>'; 31 : tochar := '?'; 32 : tochar := '@';
33 : tochar := 'A'; 34 : tochar := 'B'; 35 : tochar := 'C';
36 : tochar := 'D'; 37 : tochar := 'E'; 38 : tochar := 'F';
39 : tochar := 'G'; 40 : tochar := 'H'; 41 : tochar := 'I';
42 : tochar := 'J'; 43 : tochar := 'K'; 44 : tochar := 'L';
45 : tochar := 'M'; 46 : tochar := 'N'; 47 : tochar := 'O';
48 : tochar := 'P'; 49 : tochar := 'Q'; 50 : tochar := 'R';
51 : tochar := 'S'; 52 : tochar := 'T'; 53 : tochar := 'U';
54 : tochar := 'V'; 55 : tochar := 'W'; 56 : tochar := 'X';
57 : tochar := 'Y'; 58 : tochar := 'Z'; 59 : tochar := '[';
60 : tochar := chr(186);
61 : tochar := ']';
62 : tochar := chr(170);
63 : tochar := '_';
64 : tochar := chr(154);
65 : tochar := 'a';
66 : tochar := 'b'; 67 : tochar := 'c'; 68 : tochar := 'd';
69 : tochar := 'e'; 70 : tochar := 'f'; 71 : tochar := 'g';
72 : tochar := 'h'; 73 : tochar := 'i'; 74 : tochar := 'j';
75 : tochar := 'k'; 76 : tochar := 'l'; 77 : tochar := 'm';
78 : tochar := 'n'; 79 : tochar := 'o'; 80 : tochar := 'p';
81 : tochar := 'q'; 82 : tochar := 'r'; 83 : tochar := 's';
84 : tochar := 't'; 85 : tochar := 'u'; 86 : tochar := 'v';
87 : tochar := 'w'; 88 : tochar := 'x'; 89 : tochar := 'y';
90 : tochar := 'z'; 91 : tochar := '{'; 92 : tochar := '|';
93 : tochar := '}'; 94 : tochar := '~';
otherwise
if debug then writeln(bugfil, 'tochar error');
END; {case}
END; {tochar}
%page
FUNCTION unchar(ch : char) : integer; (* Undoes tochar *)
(*
Converts a printing character to an integer in the range 0-94.
This procedure undoes the action of "tochar".
*)
BEGIN
if ascii then
unchar := ord(ch) - 32
else case ch of
' ' : unchar := 0; '!' : unchar := 1; '"' : unchar := 2;
'#' : unchar := 3; '$' : unchar := 4; '%' : unchar := 5;
'&' : unchar := 6; '''': unchar := 7; '(' : unchar := 8;
')' : unchar := 9; '*' : unchar := 10; '+' : unchar := 11;
',' : unchar := 12; '-' : unchar := 13; '.' : unchar := 14;
'/' : unchar := 15; '0' : unchar := 16; '1' : unchar := 17;
'2' : unchar := 18; '3' : unchar := 19; '4' : unchar := 20;
'5' : unchar := 21; '6' : unchar := 22; '7' : unchar := 23;
'8' : unchar := 24; '9' : unchar := 25; ':' : unchar := 26;
';' : unchar := 27; '<' : unchar := 28; '=' : unchar := 29;
'>' : unchar := 30; '?' : unchar := 31; '@' : unchar := 32;
'A' : unchar := 33; 'B' : unchar := 34; 'C' : unchar := 35;
'D' : unchar := 36; 'E' : unchar := 37; 'F' : unchar := 38;
'G' : unchar := 39; 'H' : unchar := 40; 'I' : unchar := 41;
'J' : unchar := 42; 'K' : unchar := 43; 'L' : unchar := 44;
'M' : unchar := 45; 'N' : unchar := 46; 'O' : unchar := 47;
'P' : unchar := 48; 'Q' : unchar := 49; 'R' : unchar := 50;
'S' : unchar := 51; 'T' : unchar := 52; 'U' : unchar := 53;
'V' : unchar := 54; 'W' : unchar := 55; 'X' : unchar := 56;
'Y' : unchar := 57; 'Z' : unchar := 58; '[' : unchar := 59;
chr(186) : unchar := 60;
']' : unchar := 61;
chr(170) : unchar := 62;
'_' : unchar := 63;
chr(154) : unchar := 64;
'a' : unchar := 65;
'b' : unchar := 66; 'c' : unchar := 67; 'd' : unchar := 68;
'e' : unchar := 69; 'f' : unchar := 70; 'g' : unchar := 71;
'h' : unchar := 72; 'i' : unchar := 73; 'j' : unchar := 74;
'k' : unchar := 75; 'l' : unchar := 76; 'm' : unchar := 77;
'n' : unchar := 78; 'o' : unchar := 79; 'p' : unchar := 80;
'q' : unchar := 81; 'r' : unchar := 82; 's' : unchar := 83;
't' : unchar := 84; 'u' : unchar := 85; 'v' : unchar := 86;
'w' : unchar := 87; 'x' : unchar := 88; 'y' : unchar := 89;
'z' : unchar := 90; '{' : unchar := 91; '|' : unchar := 92;
'}' : unchar := 93; '~' : unchar := 94;
otherwise
if debug then writeln(bugfil, 'unchar error');
END; {case}
END; {unchar}
%page
FUNCTION ctl(ch : char) : char;
(*
Changes the printing characters shown below to control characters.
Used to unquote a quoted control character in a packet.
*)
BEGIN
if ascii then
ctl := chr(ord(ch) - 64)
else case ch of
'@' : ctl := chr(NUL); 'A' : ctl := chr(SOH);
'B' : ctl := chr(STX); 'C' : ctl := chr(ETX);
'D' : ctl := chr(EOT); 'E' : ctl := chr(ENQ);
'F' : ctl := chr(ACK); 'G' : ctl := chr(BEL);
'H' : ctl := chr(BS); 'I' : ctl := chr(HT);
'J' : ctl := chr(LF); 'K' : ctl := chr(VT);
'L' : ctl := chr(FF); 'M' : ctl := chr(CR);
'N' : ctl := chr(SO); 'O' : ctl := chr(SI);
'P' : ctl := chr(DLE); 'Q' : ctl := chr(DC1);
'R' : ctl := chr(DC2); 'S' : ctl := chr(DC3);
'T' : ctl := chr(DC4); 'U' : ctl := chr(NAK);
'V' : ctl := chr(SYN); 'W' : ctl := chr(ETB);
'X' : ctl := chr(CAN); 'Y' : ctl := chr(EM);
'Z' : ctl := chr(SUB); '[' : ctl := chr(ESC);
chr(186) : ctl := chr(FS);
']' : ctl := chr(GS);
chr(170) : ctl := chr(RS);
'_' : ctl := chr(US);
'?' : ctl := chr(DEL);
otherwise
if debug then writeln(bugfil, 'ctl error');
END; {case}
END; {ctl}
%page
FUNCTION unctl(ch : char) : char;
(* Changes a control character to its corresponding printing form *)
VAR
i : integer;
BEGIN
i := ord(ch);
if ascii then
unctl := chr(i + 64)
else case i of
NUL : unctl := '@'; SOH : unctl := 'A';
STX : unctl := 'B'; ETX : unctl := 'C';
EOT : unctl := 'D'; ENQ : unctl := 'E';
ACK : unctl := 'F'; BEL : unctl := 'G';
BS : unctl := 'H'; HT : unctl := 'I';
LF : unctl := 'J'; VT : unctl := 'K';
FF : unctl := 'L'; CR : unctl := 'M';
SO : unctl := 'N'; SI : unctl := 'O';
DLE : unctl := 'P'; DC1 : unctl := 'Q';
DC2 : unctl := 'R'; DC3 : unctl := 'S';
DC4 : unctl := 'T'; NAK : unctl := 'U';
SYN : unctl := 'V'; ETB : unctl := 'W';
CAN : unctl := 'X'; EM : unctl := 'Y';
SUB : unctl := 'Z'; ESC : unctl := '[';
FS : unctl := chr(186);
GS : unctl := ']';
RS : unctl := chr(170);
US : unctl := '_';
DEL : unctl := '?';
otherwise
if debug then writeln(bugfil, 'unctl error');
END; {case}
END; {unctl}
%page
FUNCTION aord(ch : char) : integer;
(* Convert a character to its ASCII ordinal value *)
BEGIN
if ascii then aord := ord(ch)
else aord := unchar(ch) + 32;
END; {aord}
FUNCTION writeopn(nampkt : packet_ptr; len : integer) : boolean;
(*
Open a file for writing during receive mode. The filename itself
is obtained from the sending Kermit in a file name packet. The
name is extracted and concatenated to dynamically create and open
it. Pascal/VS does not presently return error codes, but by
declaring the function as boolean, this feature can be readily
implemented when return codes become available. Use of column
1 for carriage control is an option.
*)
VAR
filnam : string(NAMESIZE);
crname : string(NAMESIZE + 20);
BEGIN
filnam := substr(str(nampkt@), 1, len);
crname := '$create '||filnam;
cmdnoe(crname, length(crname));
if debug then writeln(bugfil, 'Opening ', filnam);
if cc then
rewrite(rcvfil, 'FILE='||filnam|| ' MAXLEN=255 ')
else
rewrite(rcvfil, 'FILE='||filnam|| ' MAXLEN=255 NOCC');
col := 1;
writeopn := true;
END; {writeopn}
FUNCTION getnxt : boolean;
(*
Gen next file for reading when in send mode. No error codes are
returned by Pascal/VS at present, but the function returns a
boolean value, allowing implementation of such when available.
*)
BEGIN
if debug then writeln(bugfil, 'Opening ', filnam[numsent]);
reset(sndfil, 'FILE='||filnam[numsent]||' MAXLEN=255');
col := 1;
getnxt := true;
END; {getnxt}
%page
PROCEDURE rpar(data : packet_ptr);
(* Get the other side's sent-init packet. The time-out is N/A *)
BEGIN
spsiz := unchar(data@[1]); (* Maximum send packet size *)
timint := unchar(data@[2]); (* When I should time out *)
pad := unchar(data@[3]); (* Number of pads to send *)
padchar := ctl(data@[4]); (* padding char to send *)
eol := chr(unchar(data@[5])); (* end-of-line char to send *)
quote := data@[6]; (* incoming data quote char *)
if debug then (* write this to trace file *)
writeln(bugfil, 'sendinit data from other side - ',
spsiz:3, timint:3, pad:3, ord(padchar):3,
ord(eol):3, quote);
END; {rpar}
PROCEDURE spar(data : packet_ptr);
(* Fill data array with my send-init parameters *)
BEGIN
data@[1] := tochar(MAXPACK); (* my max packet size *)
data@[2] := tochar(MYTIME); (* when I should be timed out *)
data@[3] := tochar(MYPAD); (* how much padding I need *)
data@[4] := unctl(chr(MYPCHAR)); (* my pad char *)
data@[5] := tochar(MYEOL); (* my end of line *)
data@[6] := MYQUOTE; (* quote char I send *)
END; {spar}
%page
FUNCTION bufill(bufptr : packet_ptr) : integer;
(*
Get a buffer full of data from the file that is being sent.
Control characters are quoted (preceded by a '#').
*)
VAR
i : integer; (* loop index *)
t : char; (* utility character *)
BEGIN
i := 1;
while (not eof(sndfil)) and ( i < spsiz - 8) do
(* spsiz - 8 keeps the buffer from overflowing *)
BEGIN
if eoln(sndfil) then (* end of line. Quote CR and LF *)
BEGIN
(* quote the char *) bufptr@[i] := quote;
(* uncontrollify it *) bufptr@[i + 1] := unctl(chr(CR));
(* do the same for *) bufptr@[i + 2] := quote;
(* the line feed *) bufptr@[i + 3] := unctl(chr(LF));
(* bump loop ctr *) i := i + 4;
readln(sndfil); (* reset file pointer *)
col := 1; (* reset column position *)
END {if}
else
BEGIN
read(sndfil,t); (* get the next char *)
if ((col = 1) and cc) then
BEGIN
if t = '1' then (* ignore unless FF *)
BEGIN
(* quote the form feed *) bufptr@[i] := quote;
(* put char in buffer *) bufptr@[i + 1] := unctl(chr(FF));
(* bump counter *) i := i + 2;
END
END {col = 1}
(* control char or *) else if (ord(t) < SP) or (t = chr(DEL))
or (t = quote) then
(* quote? *) BEGIN
(* yes, so quote it *) bufptr@[i] := quote;
(* uncontrollify it *) if t <> quote then t := unctl(t);
(* put char in buffer *) bufptr@[i + 1] := t;
(* bump counter *) i := i + 2;
END
else
BEGIN
bufptr@[i] := t; (* put char in buffer *)
i := i + 1; (* bump counter *)
END;
col := col + 1; (* advance column counter *)
END; {else}
END; {while}
bufill := i - 1; (* return count *)
END; {bufill}
%page
PROCEDURE bufemp(buffer : packet_ptr; len : integer);
(* Get data from incoming packet into a file *)
VAR
i : integer; (* counter *)
t : char; (* utility character *)
BEGIN
i := 1;
while i <= len do (* loop thru character field *)
BEGIN
t := buffer@[i]; (* get character *)
if t = MYQUOTE then (* next char must be unquoted *)
BEGIN
i := i + 1; (* bump counter *)
t := buffer@[i]; (* get quoted char *)
case t of
(* it was a real quote *) MYQUOTE : write(rcvfil, t);
(* CR, so assume newline *) 'M' : begin
writeln(rcvfil);
(* reset column marker *) col := 1;
end;
(* LF, don't pass *) 'J' : ;
(* FF, so make new page *) 'L' : begin
page(rcvfil);
col := col + 1;
end;
(* expand the tabs *) 'I' : repeat
(* assume stops at 1, 9, 17, etc. *) write(rcvfil, ' ');
col := col + 1;
until (col mod 8 = 1);
otherwise
(* make a control character *) begin
write(rcvfil, ctl(t));
(* increment column marker *) col := col + 1;
end;
END; {case}
END {if}
else
begin
write(rcvfil, t); (* put character into file *)
col := col + 1; (* increment column marker *)
end;
i := i + 1;
END; {while}
END; {bufemp}
%page
FUNCTION rpack(var len, num : integer; data : packet_ptr) : char;
(* Read a packet being sent. Compute check sum, return packet type *)
LABEL 10; (* Heavens! a GOTO - for resynchronization *)
VAR
i, chksum : integer; (* counter, check sum *)
done : boolean; (* packet read if true *)
t, class : char; (* utility char, packet type *)
BEGIN
if debug then writeln(bugfil, 'rpack'); (* debug, trace file *)
while t <> chr(SOH) do read(t); (* look for synch char SOH *)
if debug then write(bugfil, t); (* save in debugging file *)
done := false; (* not yet done *)
10: while not done do
BEGIN
read(t); (* get char *)
if debug then write(bugfil, t); (* save in trace file *)
if t = chr(SOH) then goto 10; (* if synch, start again *)
chksum := aord(t); (* accumulate check sum *)
len := unchar(t) - 3; (* get length of packet *)
read(t); (* get char *)
if debug then write(bugfil, t); (* save in trace file *)
if t = chr(SOH) then goto 10; (* resynchronize *)
chksum := chksum + aord(t); (* accumulate check sum *)
num := unchar(t); (* get packet number *)
read(t); (* get char *)
if debug then write(bugfil, t); (* save in trace file *)
if t = chr(SOH) then goto 10; (* resynchronize *)
chksum := chksum + aord(t); (* accumulate sum *)
class := t; (* get packet type *)
for i := 1 to len do (* get the actual data *)
BEGIN
(* get char *) read(t);
(* save in trace file *) if debug then write(bugfil, t);
(* resynchronize *) if t = chr(SOH) then goto 10;
(* accumulate check sum *) chksum := chksum + aord(t);
(* store data *) data@[i] := t;
END;
read(t); (* get sender's check sum *)
(* resynchronize *) if t = chr(SOH) then goto 10;
(* save in trace *) if debug then write(bugfil, t);
done := true; (* end of packet *)
END; {while}
if t = tochar(checksum(chksum)) then rpack := class else
rpack := 'E'; (* compare check sums, return 'E' if bad *)
if debug then writeln(bugfil); (* flush line to trace file *)
END; {rpack}
%page
PROCEDURE spack(class : char; num, len : integer; data : packet_ptr);
(* Send a packet to the other side *)
TYPE
buffer = packed array[1..100] of char;
VAR
i : integer; (* counter *)
chksum : integer; (* packet checksum *)
bufp : @buffer; (* pointer to buffer *)
BEGIN
if debug then writeln(bugfil, 'spack'); (* save in trace *)
if pad > 0 then (* send padding if needed *)
for i := 1 to pad do write(padchar);
new(bufp); (* make space *)
bufp@[1] := chr(SOH); (* synch character *)
bufp@[2] := tochar(len + 3); (* char representation of length *)
chksum := aord(bufp@[2]); (* char representation of check sum *)
bufp@[3] := tochar(num); (* char representation of packet number *)
chksum := chksum + aord(bufp@[3]); (* accumulate check sum *)
bufp@[4] := class; (* packet type *)
chksum := chksum + aord(class); (* accumulate check sum *)
for i := 1 to len do (* accumulate data and check sum *)
BEGIN
bufp@[4 + i] := data@[i];
chksum := chksum + aord(data@[i]);
END;
bufp@[len + 4 + 1] := tochar(checksum(chksum));
(* char representation of check sum *)
bufp@[len + 4 + 2] := eol; (* end of line wanted by other end *)
for i := 1 to (len+4+1) do write(bufp@[i]);
(* send it out to other side *)
writeln(bufp@[len+4+2]); (* IMPORTANT! Must flush output in MTS *)
if debug then (* save the packet in the trace file *)
BEGIN
for i := 1 to (len+4+2) do write(bugfil, bufp@[i]);
writeln(bugfil); (* flush to file *)
END;
END; {spack}
%page
FUNCTION recsw : boolean;
(* State table switcher for receiving files *)
VAR
done : boolean; (* no more files to receive if true *)
FUNCTION rinit : char;
(* Receive initialization from sender *)
VAR
len, num : integer; (* packet length, number *)
BEGIN
if debug then writeln(bugfil, 'rinit');
if numtry > MAXTRY then (* too many tries, so abort *)
rinit := 'A'
else
BEGIN
(* bump try count *) numtry := numtry + 1;
(* get a packet *) case rpack(len, num, recpkt) of
(* got a send-init *) 'S' : BEGIN
(* retrieve parameters from sender *) rpar(recpkt);
(* fill up packet with my info *) spar(packet);
(* ACK with my packet *) spack('Y', n, 6, packet);
(* save old try count *) oldtry := numtry;
(* start a new counter *) numtry := 0;
(* bump count, mod 64 *) n := (n + 1) mod 64;
(* return file-send state *) rinit := 'F';
END; {S}
(* didn't get packet *) 'E' : rinit := state; (* keep waiting *)
(* some other type, abort *) otherwise
rinit := 'A';
END; {case}
END; {else}
END; {rinit}
%page
FUNCTION rfile : char;
(* Receive file name *)
VAR
num, len : integer; (* packet number, length *)
k : integer; (* utility integer *)
BEGIN
if debug then writeln(bugfil, 'rfile');
if numtry > MAXTRY then (* abort if too many tries *)
rfile := 'A'
else
BEGIN
(* bump count *) numtry := numtry + 1;
(* get a packet *) case rpack(len, num, recpkt) of
(* send-init, maybe ACK *) 'S' : BEGIN
(* has been lost *) if oldtry > MAXTRY then
(* if too many tries, abort *) rfile := 'A'
else
BEGIN
(* bump oldtry count as well *) oldtry := oldtry + 1;
(* previous packet mod 64 ? *) k := n - 1;
if k < 0 then k := 63;
(* yes, so ACK it again *) if num = k then
BEGIN
(* send our send-init packet *) spar(packet);
spack('Y', num,
6, packet);
(* reset try counter *) numtry := 0;
(* stay in this state *) rfile := state;
END
else
(* not previous packet, abort *) rfile := 'A';
END; {else}
END; {S}
(* end-of-file *) 'Z' : BEGIN
if oldtry > MAXTRY then
rfile := 'A'
else
BEGIN
oldtry := oldtry + 1;
(* previous packet, mod 64 ? *) k := n - 1;
if k < 0 then k := 63;
(* yes, so ACK it again *) if num = k then
BEGIN
spack('Y', num, 0,
packet);
numtry := 0;
(* stay in this state *) rfile := state;
END
else
(* not previous packet, abort *) rfile := 'A';
END
END; {Z}
(* file-header *) 'F' : BEGIN
(* what we really want so the *) if num <> n then
(* packet number must be correct *) rfile := 'A'
else
BEGIN
(* try to open a new file *) if not writeopn(recpkt, len) then
rfile := 'A'
else
(* if OK then *) BEGIN
(* ACK the file header *) spack('Y', n, 0, packet);
(* reset counters *) oldtry := numtry;
numtry := 0;
(* bump packet number mod 64 *) n := (n + 1) mod 64;
(* switch to data packet *) rfile := 'D';
END;
END;
END; {F}
(* break transmission *) 'B' : BEGIN
(* need correct packet number *) if num <> n then
rfile := 'A'
else
BEGIN
(* say OK *) spack('Y', n, 0, packet);
(* switch to complete state *) rfile := 'C';
END;
END; {B}
(* souldn't get packet *) 'E' : rfile := state; (* keep trying *)
(* something else, abort *) otherwise
rfile := 'A';
END; {case}
END;
END; {rfile}
FUNCTION rdata : char;
(* Receive data *)
VAR
num, len : integer; (* packet number, length *)
k : integer; (* utility integer *)
BEGIN
if debug then writeln(bugfil, 'rdata');
if numtry > MAXTRY then (* abort if too many tries *)
rdata := 'A'
else
BEGIN
numtry := numtry + 1; (* bump try counter *)
(* get packet *) case rpack(len, num, recpkt) of
(* got a data packet *) 'D' : BEGIN
(* looks like wrong number *) if num <> n then
BEGIN
(* if too many tries, then quit *) if oldtry > MAXTRY then
rdata := 'A'
else
BEGIN
(* bump oldtry counter *) oldtry := oldtry + 1;
(* see if we have previous packet again *) k := n - 1;
if k < 0 then k := 63;
(* yes, got previous one *) if num = k then
BEGIN
(* re-ACK the packet *) spack('Y', num,
0, packet);
(* reset try counter *) numtry := 0;
(* stay in D, don't write out data *) rdata := state;
END
else
(* Sorry, wrong number *) rdata := 'A';
END;
END; { num <> n }
(* write the packet to file *) bufemp(recpkt, len);
(* acknowledge the packet *) spack('Y', n, 0, packet);
(* reset the counters *) oldtry := numtry;
numtry := 0;
(* count packets, mod 64 *) n := (n + 1) mod 64;
(* stay in this state *) rdata := 'D';
END; {D}
(* got a file header *) 'F' : BEGIN
(* too many, so quit *) if oldtry > MAXTRY then
rdata := 'A'
else
BEGIN
(* bump try counter *) oldtry := oldtry + 1;
(* see if previous packet *) k := n - 1;
if k < 0 then k := 63;
(* yes, so ACK it again *) if num = k then
BEGIN
spack('Y', num, 0,
packet);
numtry := 0;
(* stay in data state *) rdata := state;
END
else
(* not previous packet so abort *) rdata := 'A';
END;
END; {Z}
'Z' : BEGIN
(* must have right packet *) if num <> n then
rdata := 'A'
else
BEGIN
(* OK, so ACK it *) spack('Y', n, 0, packet);
(* close the file *) close(rcvfil);
(* bump packet counter *) n := (n + 1) mod 64;
(* go back to receive file state *) rdata := 'F';
END;
END;
(* nothing, keep waiting *) 'E' : rdata := state;
(* some other type, *) otherwise
(* so abort *) rdata := 'A';
END; {case}
END;
END; {rdata}
BEGIN {recsw}
done := false; (* initialize *)
state := 'R'; (* always start in receive state *)
n := 0; (* initialize message number *)
numtry := 0; (* no tries yet *)
while not done do (* do until done *)
case state of
'D' : state := rdata; (* data receive state *)
'F' : state := rfile; (* file receive state *)
'R' : state := rinit; (* send initiate state *)
'C' : BEGIN (* completed state *)
recsw := true;
done := true;
END;
'A' : BEGIN (* abort state *)
recsw := false;
done := true;
END;
END; {case}
END; {recsw}
%page
FUNCTION sendsw : boolean;
(* State table switcher for sending files *)
VAR
done : boolean; (* indicates that sending is finished *)
FUNCTION sinit : char;
(* Send my parameters and get other side's back *)
VAR
num, len : integer; (* packet number, length *)
BEGIN {function sinit}
if debug then writeln(bugfil, 'sinit');
if numtry > MAXTRY then sinit := 'A' (* too many tries *)
else
BEGIN
numtry := numtry + 1; (* bump try counter *)
spar(packet); (* fill up with init info *)
spack('S', n, 6, packet); (* send it out *)
case rpack(len, num, recpkt) of (* get reply *)
(* NAK packet *) 'N', 'E' : sinit := state; (* just stay in state *)
(* ACK packet *) 'Y' : BEGIN
(* wrong ACK, stay in state *) if n <> num then
sinit := state
else
BEGIN
(* get other side's init info *) rpar(recpkt);
(* check and set defaults *) if eol = chr(NUL)
then eol := chr(CR);
if quote = chr(NUL)
then quote := MYQUOTE;
(* reset try counter *) numtry := 0;
(* bump packet count *) n := (n + 1) mod 64;
(* open file to be sent *) if getnxt then
(* if open OK go to next state *) sinit := 'F'
(* no good, so give up *) else sinit := 'A';
END; {else}
END; {'Y'}
(* unknown, abort *) otherwise
sinit := 'A';
END; {case}
END; {else}
END; {sinit}
%page
FUNCTION sfile : char;
(* Send file name *)
VAR
num, len, l : integer; (* packet number, len, stringlength *)
c : char; (* utility character *)
BEGIN
if debug then writeln(bugfil, 'sfile');
if numtry > MAXTRY (* too many tries, give up *)
then sfile := 'A'
else
BEGIN
numtry := numtry + 1; (* bump try counter *)
len := 0; (* set packet length to zero *)
l := length(filnam[numsent]); (* length of filename *)
while (len < l) and (len < NAMESIZE) do
BEGIN
len := len + 1; (* accumulate length *)
(* stash away the name itself *) packet@[len] :=
(* in upper case *) toupper(filnam[numsent][len]);
END;
(* send it out *) spack('F', n, len, packet);
(* get reply *) c := rpack(len, num, recpkt);
case c of
(* NAK or ACK *) 'N', 'Y' : BEGIN
if c = 'N' then
(* as before, stay in this state *) BEGIN
(* unless NAK for next packet *) num := num - 1;
(* which is like an ACK for this packet *) if num < 0 then num := 63;
END;
(* wrong count so stay in this state *) if n <> num then sfile := state
else
BEGIN
(* reset counters *) numtry := 0;
(* bump packet count *) n := (n + 1) mod 64;
(* get first data from file *) size := bufill(packet);
(* switch to data state *) sfile := 'D';
END;
END;
(* receive failure *) 'E' : sfile := state; (* just stay here *)
otherwise
(* unknown, abort *) sfile := 'A';
END; {case}
END; {else}
END; {sinit}
%page
FUNCTION sdata : char;
VAR
num, len : integer; (* packet number, length *)
c : char; (* utility character *)
BEGIN
if debug then writeln(bugfil, 'sdata');
if numtry > MAXTRY then sdata := 'A' (* abort if too many *)
else
BEGIN
numtry := numtry + 1; (* bump try counter *)
spack('D', n, size, packet); (* send a data packet *)
c := rpack(len, num, recpkt); (* get the reply *)
case c of
'N', 'Y' : BEGIN (* NAK or ACK *)
(* respond to NAK *) if c = 'N' then
BEGIN
num := num - 1;
if num < 0 then num := 63;
END;
(* just stay in this state *) if n <> num then sdata := state
(* unless NAK is for next packet *) else
(* which is like an ACK for this one *) BEGIN
(* reset try counter *) numtry := 0;
(* bump packet count *) n := (n + 1) mod 64;
if not eof(sndfil) then
BEGIN
(* get data from file if not at end *) size :=
bufill(packet);
(* stay in data state *) sdata := 'D';
END
else
(* EOF, so switch to that state *) sdata := 'Z';
END;
END;
(* receive failure *) 'E' : sdata := state; (* stay in state *)
otherwise
(* anything else, abort *) sdata := 'A';
END; {case}
END; {else}
END; {sdata}
%page
FUNCTION seof : char;
(* Send enf-of-file *)
VAR
num, len : integer; (* packet number, length *)
c : char; (* utility char *)
BEGIN
if debug then writeln(bugfil, 'seof');
if numtry > MAXTRY then (* too many, quit *)
seof := 'A'
else
BEGIN
numtry := numtry + 1; (* bump counter *)
spack('Z', n, 0, packet); (* send Z packet *)
c := rpack(len, num, recpkt); (* get reply *)
case c of
(* ACK or NAK *) 'N', 'Y' : BEGIN
(* NAK, fail unless for *) if c = 'N' then
(* previous packet *) BEGIN
(* then fall thru *) num := num - 1;
if num < 0 then num := 63;
END;
(* wrong, so stay in state *) if n <> num then seof := state
else
BEGIN
(* reset counter *) numtry := 0;
(* increment count *) n := (n + 1) mod 64;
if debug then
writeln(bugfil,
'closing - ',
filnam[numsent]);
(* close the file *) close(sndfil);
(* increment number of files sent *) numsent := numsent + 1;
(* get new one if more to go *) if numsent < nfiles then
BEGIN
(* and go back to filename state *) if getnxt then
seof := 'F'
else
(* unless failure in file open *) seof := 'B'
END
(* no more files, so set break state *) else seof := 'B';
END; {else}
END; {N, Y}
(* error, stay in state *) 'E' : seof := state;
(* unknown, abort *) otherwise
seof := 'A';
END; {case}
END; { else }
END; {seof}
%page
FUNCTION sbreak : char;
(* send a break *)
VAR
num, len : integer; (* packet number, length *)
c : char; (* utility char *)
BEGIN
if debug then writeln(bugfil, 'sbreak');
if numtry > MAXTRY then
sbreak := 'A' (* abort if too many *)
else
BEGIN
(* bump counter *) numtry := numtry + 1;
(* send a break *) spack('B', n, 0, packet);
(* look at reply *) c := rpack(len, num, recpkt);
case c of
(* see if ACK for this *) 'N', 'Y' : BEGIN
(* packet or NAK for previous *) if c = 'N' then
BEGIN
num := num - 1;
if num < 0 then num := 63;
END;
(* if wrong, then stay in state *) if n <> num then sbreak := state
else
BEGIN
(* reset counter *) numtry := 0;
(* bump packet count *) n := (n + 1) mod 64;
(* switch to complete state *) sbreak := 'C';
END;
END;
(* receive failure *) 'E' : sbreak := state; (* stay in state *)
otherwise
(* unknown, abort *) sbreak := 'A';
END; {case}
END; { else }
END; {sbreak}
%page
BEGIN {sendsw}
done := false; (* not done yet *)
state := 'S'; (* send initiate is the start state *)
n := 0; (* initialize message number *)
numtry := 0; (* no tries yet *)
while not done do
case state of
'D' : state := sdata; (* data send state *)
'F' : state := sfile; (* send file name *)
'Z' : state := seof; (* end of file *)
'S' : state := sinit; (* send-init *)
'B' : state := sbreak; (* break-send *)
'C' : BEGIN sendsw := true; done := true END;
(* complete *)
'A' : BEGIN sendsw := false; done := true END;
(* abort *)
otherwise
BEGIN sendsw := false; done := true END;
(* unknown, so fail *)
END; {case}
END; {sendsw}
%page
PROCEDURE init; (* Initialize parameters *)
BEGIN
delay[1] := 0; (* set up initial packet delay *)
delay[2] := SNDINIT_DLY;
ascii := false; (* We are using ASCII if true *)
debug := false; (* For program development *)
if debug then (* creating temporary debug file *)
BEGIN
(* cmdnoe('$create -debug', 14); *)
rewrite(bugfil, 'FILE=-debug');
END;
reset(input, 'FILE=*msource* Interactive MAXLEN=255');
rewrite(output, 'FILE=*msink* MAXLEN=255');
(* make wide as possible *)
new(packet); (* Point to packet *)
new(recpkt); (* make the space needed *)
eol := chr(CR); (* EOL for outgoing packets *)
quote := MYQUOTE; (* Standard control-quote char *)
pad := 0; (* No padding *)
padchar := chr(NUL); (* Use null if any padding wanted *)
END;
%page
BEGIN {main}
datetime(date, time);
writeln('Mathematical Reviews - Kermit on MTS.');
writeln('The date is ', date, '. The time is ', time, '.');
writeln;
writeln('For help see the file SJ1K:KERMIT.DOC.');
writeln;
init; (* initialize all parameters *)
writeln('Enter command - (r)eceive/(s)end:');
readln(command); (* get the command *)
command := toupper(command); (* convert to upper case *)
writeln('Is column 1 reserved for carriage control (y/n)?');
readln(ccinfo);
cc := (toupper(ccinfo) = 'Y');
if command = 'S' then (* get the files to send *)
BEGIN
nfiles := 0;
writeln('Enter file names one at a time.');
writeln('Terminate list with carriage return.');
writeln;
repeat
writeln('File to send:');
nfiles := nfiles + 1;
readln(filnam[nfiles]);
until (nfiles >= MAXFILES) or (filnam[nfiles] = '')
END;
setsys; (* set the terminal so Kermit will work *)
case command of
'S' : BEGIN (* send files *)
writeln;
write('Exit to your system, set IBM mode ON,');
writeln(' and initiate RECEIVE-FILE mode.');
writeln(chr(DC1)); (* write an XON *)
twait(0, delay); (* wait a while *)
numsent := 1; (* none sent yet *)
if sendsw = false then (* now go to send switcher *)
if debug then
writeln(bugfil, 'Send failed at - ',
filnam[numsent])
else if debug then writeln(bugfil, 'Send OK');
END;
'R' : BEGIN (* receive files *)
writeln;
write('Exit to your system, set IBM mode ON,');
writeln(' and initiate SEND-FILE mode.');
if recsw = false then (* go to receive state switcher *)
if debug then writeln(bugfil, 'Receive failed.')
else if debug then writeln(bugfil, 'Receive OK.');
END;
otherwise (* not a valid command *)
writeln('Invalid command given.');
END; {case}
close(bugfil);
resetsys; (* return terminal to original state *)
END. {Kermit}