PROGRAM Kermit(input,output,file3,file4,file5, file6,file7,file8,file9,filen,filet); LABEL 9999; { used only to simulate a "halt" instruction } CONST bufsize=128; lf=12B; return=15B; formfeed=14B; controlbar=28; CTRLC=3; mask= 177B; { standard file descriptors. subscripts in open, etc. } STDIN = 1; { these are not to be changed } STDOUT = 2; lineout = 3; linein = 4; { other io-related stuff } IOERROR = 0; { status values for open files } IOAVAIL = 1; IOREAD = 2; IOWRITE = 3; MAXOPEN = 9; { maximum number of open files } { universal manifest constants } ENDFILE = -1; ENDSTR = 0; { null-terminated strings } MAXSTR = 100; { longest possible string } CONLENGTH = 20; { length of constant string } FILENAMELENGTH = 17; { length of file name for Bind } MAXERRORS = 50; { maximum number of errors kept if remote } { ascii character set in decimal } BACKSPACE = 8; TAB = 9; NEWLINE = 10; BLANK = 32; EXCLAM = 33; { ! } DQUOTE = 34; { " } SHARP = 35; { # } DOLLAR = 36; { $ } PERCENT = 37; { % } AMPER = 38; { & } SQUOTE = 39; { ' } ACUTE = SQUOTE; LPAREN = 40; { ( } RPAREN = 41; { ) } STAR = 42; { * } PLUS = 43; { + } COMMA = 44; { , } MINUS = 45; { - } DASH = MINUS; PERIOD = 46; { . } SLASH = 47; { / } COLON = 58; { : } SEMICOL = 59; { ; } LESS = 60; { < } EQUALS = 61; { = } GREATER = 62; { > } QUESTION = 63; { ? } ATSIGN = 64; { @ } LBRACK = 91; { [ } BACKSLASH = 92; { \ } ESCAPE = BACKSLASH; { changed - used to be @ } RBRACK = 93; { ] } CARET = 94; { ^ } UNDERLINE = 95; { _ } GRAVE = 96; { ` } LETA = 97; { lower case ... } LETB = 98; LETC = 99; LETD = 100; LETE = 101; LETF = 102; LETG = 103; LETH = 104; LETI = 105; LETJ = 106; LETK = 107; LETL = 108; LETM = 109; LETN = 110; LETO = 111; LETP = 112; LETQ = 113; LETR = 114; LETS = 115; LETT = 116; LETU = 117; LETV = 118; LETW = 119; LETX = 120; LETY = 121; LETZ = 122; LBRACE = 123; { left brace } BAR = 124; { | } RBRACE = 125; { right brace } TILDE = 126; { ~ } SOH = 1; (* ascii SOH character *) CR = 13; (* CR *) DEL = 127; (* rubout *) DEFTRY = 10; (* default for number of retries *) DEFTIMEOUT = 12; (* default time out *) MAXPACK = 94; (* max is 94 ~ - ' ' *) DEFDELAY = 5; (* delay before sending first init *) NUMPARAM = 6; (* number of parameters in init packet *) DEFQUOTE = SHARP; (* default quote character *) DEFPAD = 0; (* default number OF padding chars *) DEFPADCHAR = 0; (* default padding character *) DEFDUPLEX = false; (* default duplex is full duplex *) (* SYSTEM DEPENDENT *) DEFEOL = CR; DEFEOLTYPE = 2; (* 1 = LineFeed 2 = CrLf 3 = Just Cr *) FLEN1 = 8; FLEN2 = 8; PFILE = 'KERMIT.P '; TRACEFILE = 'KERMIT.T '; TEMPFILE = 'TEMP.K '; lp = 'LP: '; NUMBUFFERS = 5; (* Number of buffers *) (* packet types *) TYPEB = 66; (* ord('B') *) TYPED = 68; (* ord('D') *) TYPEE = 69; (* ord('E') *) TYPEF = 70; (* ord('F') *) TYPEN = 78; (* ord('N') *) TYPES = 83; (* ord('S') *) TYPET = 84; (* ord('T') *) TYPEY = 89; (* ord('Y') *) TYPEZ = 90; (* ord('Z') *) MAXCMD = 10; TYPE character = -128..127; { byte-sized. ascii + other stuff } string = ARRAY [1..MAXSTR] OF character; mstring = PACKED ARRAY [1..FILENAMELENGTH] OF char; vstring = RECORD len : integer; ch : ARRAY [1..MAXSTR] OF char; END; cstring = PACKED ARRAY [1..CONLENGTH] OF char; filedesc = IOERROR..MAXOPEN; (* Data Types for Kermit *) Packet = RECORD mark : character; (* SOH character *) count: character; (* # of bytes following this field *) seq : character; (* sequence number modulo 64 *) ptype: character; (* d,y,n,s,b,f,z,e,t packet type *) data : string; (* the actual data *) (* chksum is last validchar in data array *) (* eol is added, not considered part of packet proper *) END; Command = (Transmit,Receive,Print,SetParm,Invalid); KermitStates = (FileData,Init,Break,FileHeader,EOFile,Complete,Abort); EOLtype = (LineFeed,CrLf,JustCr); Words = (Low,High); Stats = ARRAY [Low..High] OF integer; Ppack = 1..NUMBUFFERS; CType = RECORD check: integer; PacketPtr : integer; i : integer; fld : integer; t : character; finished : boolean; restart : boolean; control : boolean; good : boolean; END; InType = (abortnow,nothing,CRin); VAR ch : char; done : boolean; HalfDuplex : boolean; BindStatus : integer; file3 : text; { output to other computer } file4 : text; { input from other computer } file5 : text; { assigned to a file to send or receive } file6 : text; file7 : text; file8 : text; file9 : text; filen : text; { check for a file's existance } filet : text; { trace output } filemode : ARRAY [1..MAXOPEN] OF IOERROR..IOWRITE; cmdargs : 0..MAXCMD; cmdlin : string; cmdidx : ARRAY [1..MAXCMD] OF 1..MAXSTR; (* Variables for Kermit *) aline : string; DiskFile : filedesc; SaveState : kermitstates; NextArg : integer; (* next argument to process *) Local : boolean; (* local/remote flag *) MaxTry : integer; n,J : integer; (* packet number *) NumTry : integer; (* times this packet retried *) OldTry : integer; Pad : integer; (* padding to send *) MyPad : integer; (* number of padding characters I need *) PadChar : character; MyPadChar: character; RunType : command; State : kermitstates; (* current state of the automaton *) MyTimeOut: integer; (* when i want to be timed out *) TheirTimeOut : integer; Delay : integer; SizeRecv, SizeSend : integer; SendEOL, SendQuote : character; myEOL,myQuote: character; EOLforFile : EOLtype; ParmFile : string; NumSendPacks : integer; NumRecvPacks : integer; NumACK : integer; NumNAK : integer; NumACKrecv : integer; NumNAKrecv : integer; NumBADrecv : integer; RunTime: integer; ChInFile, ChInPack : Stats; Verbosity: boolean; (* true to print verbose messages *) Trace: boolean; (* true to write trace info in KERMIT.T file *) OneWayOnly : boolean; (* used for testing *) Debug : boolean; TtyMode : (Cooked,Raw); KeptErrors : ARRAY [1..MAXERRORS] OF cstring; (* keep errors if remote *) NumKeptErrors : integer; Buf : ARRAY [1..NUMBUFFERS] OF packet; ThisPacket : Ppack; (* current packet being sent *) LastPacket : Ppack; (* last packet sent *) CurrentPacket : Ppack; (* current packet received *) NextPacket : Ppack; (* next packet being received *) InputPacket : Ppack; (* save input to do debug *) TOPacket : packet; (* Time_Out Packet *) TimeLeft : integer; (* until Time_Out *) FromConsole : InType; (* Input from Console during receive *) PackControl : CType; (* variables for receive packet routine *) { prims -- primitive functions and procedures } PROCEDURE SYSINIT; ALIEN; { System dependent initialize } FUNCTION CONNECT(DUPLEX : BOOLEAN): BOOLEAN; ALIEN; { Connect to remote host computer--we are local. Echange characters between host and terminal until user presses escape code. DUPLEX is false for full duplex, true for half duplex. Return false if this Kermit is host only (no connection possible) } FUNCTION GETIN(VAR TIMEREMAINING : INTEGER; VAR FROMCONSOLE : INTYPE): CHARACTER; ALIEN; { If connected, get character from host; otherwise, get character from terminal. Decrement timeremaining for each full second you wait; give up when timeleft gets to zero. If connected to host computer, and user types a character, set fromconsole accordingly } PROCEDURE XMTCHAR(C : CHAR); ALIEN; { If connected, send character to host; otherwise send character to terminal } PROCEDURE SYSFINISH; ALIEN; { If connected, disconnect. System depedent clean up. } PROCEDURE SLEEP(T: INTEGER); ALIEN; { Delay for T seconds } PROCEDURE TTYRAW; ALIEN; { For host mode--put terminal into character by character mode. When in this mode, only GETIN and XMTCHAR are used to talk to the tty } PROCEDURE TTYCOOKED; ALIEN; { Return terminal to normal I/O mode } PROCEDURE FLUSH; ALIEN; { Flush any pending output } PROCEDURE FILECREATE(FILENAME : MSTRING); ALIEN; { Create a file } PROCEDURE FIXNAME(VAR FILENAME : STRING); ALIEN; { Fix up file name before sending it to other Kermit. Argument is 1 character per word in least significant bits } FUNCTION BITWISE(i,j,result00,result01,result10,result11:integer):integer; { Perform bit-wise logical operation on two integers given the truth table: | bit in j=0 | bit in j=1 | ------------+--------------+--------------+ bit in i=0 | result00 | result01 | ------------+--------------+--------------+ bit in i=1 | result10 | result11 | ------------+--------------+--------------+ For negative numbers, use the fact that on a two's complement machine the bit-wise NOT of an integer "n" is "-1 - n". This works on machines that are not two's complement also, as long as we consistently use "-1 - n" as the NOT, and know how to interpret negative results. } VAR bit, result: integer; BEGIN if i < 0 then BITWISE := BITWISE(-1-i,j,result10,result11,result00,result01) else if j < 0 then BITWISE := BITWISE(i,-1-j,result01,result00,result11,result10) else if result00 <> 0 then BITWISE := -1 - BITWISE(i,j,0,1-result01,1-result10,1-result11) else BEGIN result := 0; bit := 1; WHILE (i > 0) AND (j > 0) DO BEGIN IF odd(i) THEN IF odd(j) THEN result := result + bit*result11 ELSE result := result + bit*result10 ELSE IF odd(j) THEN result := result + bit*result01; i := i DIV 2; j := j DIV 2; bit := bit + bit; END; BITWISE := result + bit*(i*result10 + j*result01); END; END; FUNCTION IAND(i,j:integer):integer; BEGIN IAND := BITWISE(i,j,0,0,0,1); END; FUNCTION IOR(i,j:integer):integer; BEGIN IOR := BITWISE(i,j,0,1,1,1); END; PROCEDURE fdbind(fd: filedesc; intname: mstring); BEGIN CASE fd OF 1: bind(input,intname,BindStatus); 2: bind(output,intname,BindStatus); 3: bind(file3,intname,BindStatus); 4: bind(file4,intname,BindStatus); 5: bind(file5,intname,BindStatus); 6: bind(file6,intname,BindStatus); 7: bind(file7,intname,BindStatus); 8: bind(file8,intname,BindStatus); 9: bind(file9,intname,BindStatus); END; END; PROCEDURE fdclose(fd: filedesc); BEGIN CASE fd OF 1: close(input); 2: close(output); 3: close(file3); 4: close(file4); 5: close(file5); 6: close(file6); 7: close(file7); 8: close(file8); 9: close(file9); END; END; FUNCTION fdeof(fd: filedesc): boolean; BEGIN CASE fd OF 1: fdeof := eof(input); 2: fdeof := eof(output); 3: fdeof := eof(file3); 4: fdeof := eof(file4); 5: fdeof := eof(file5); 6: fdeof := eof(file6); 7: fdeof := eof(file7); 8: fdeof := eof(file8); 9: fdeof := eof(file9); END; END; FUNCTION fdeoln(fd: filedesc): boolean; BEGIN CASE fd OF 1: fdeoln := eoln(input); 2: fdeoln := eoln(output); 3: fdeoln := eoln(file3); 4: fdeoln := eoln(file4); 5: fdeoln := eoln(file5); 6: fdeoln := eoln(file6); 7: fdeoln := eoln(file7); 8: fdeoln := eoln(file8); 9: fdeoln := eoln(file9); END; END; PROCEDURE fdread(fd: filedesc; VAR ch: char); BEGIN CASE fd OF 1: read(input,ch); 2: read(output,ch); 3: read(file3,ch); 4: read(file4,ch); 5: read(file5,ch); 6: read(file6,ch); 7: read(file7,ch); 8: read(file8,ch); 9: read(file9,ch); END; END; PROCEDURE fdreadln(fd: filedesc); BEGIN CASE fd OF 1: readln(input); 2: readln(output); 3: readln(file3); 4: readln(file4); 5: readln(file5); 6: readln(file6); 7: readln(file7); 8: readln(file8); 9: readln(file9); END; END; PROCEDURE fdreset(fd: filedesc); BEGIN CASE fd OF 1: reset(input); 2: reset(output); 3: reset(file3); 4: reset(file4); 5: reset(file5); 6: reset(file6); 7: reset(file7); 8: reset(file8); 9: reset(file9); END; END; PROCEDURE fdrewrite(fd: filedesc); BEGIN CASE fd OF 1: rewrite(input); 2: rewrite(output); 3: rewrite(file3); 4: rewrite(file4); 5: rewrite(file5); 6: rewrite(file6); 7: rewrite(file7); 8: rewrite(file8); 9: rewrite(file9); END; END; PROCEDURE fdwrite(fd: filedesc; ch: char); BEGIN CASE fd OF 1: write(input,ch); 2: IF TtyMode = Cooked THEN write(output,ch) ELSE IF Trace THEN write(filet,ch); 3: write(file3,ch); 4: write(file4,ch); 5: write(file5,ch); 6: write(file6,ch); 7: write(file7,ch); 8: write(file8,ch); 9: write(file9,ch); END; END; PROCEDURE fdwriteln(fd: filedesc); BEGIN CASE fd OF 1: writeln(input); 2: IF TtyMode = Cooked THEN writeln(output) ELSE IF Trace THEN writeln(filet); 3: writeln(file3); 4: writeln(file4); 5: writeln(file5); 6: writeln(file6); 7: writeln(file7); 8: writeln(file8); 9: writeln(file9); END; END; PROCEDURE WriteCharacter; BEGIN write(ch); END; PROCEDURE stiphalt; (* used by external procedures for halt *) BEGIN GOTO 9999; END; { initio -- initialize open file list } PROCEDURE initio; VAR i : filedesc; BEGIN filemode[STDIN] := IOREAD; filemode[STDOUT] := IOWRITE; filemode[lineout] := IOWRITE; filemode[linein] := IOREAD; { connect STDOUT to user's terminal ... } fdrewrite(STDOUT); { initialize rest of files } FOR i := linein+1 TO MAXOPEN DO filemode[i] := IOAVAIL; END; { getc (UCB) -- get one character from standard input } FUNCTION getc (VAR c : character) : character; VAR ch : char; BEGIN IF eof THEN c := ENDFILE ELSE IF eoln THEN BEGIN readln; c := NEWLINE END ELSE BEGIN read(ch); c := ord(ch) END; getc := c END; { Copyright (C) 1981 by Bell Laboratories, Inc., and Whitesmiths Ltd. } { getcf (UCB) -- get one character from file } FUNCTION getcf (VAR c: character; fd : filedesc) : character; VAR ch : char; BEGIN IF (filemode[fd] <> IOREAD) THEN BEGIN writeln('called getcf without file.mode=IOREAD'); stiphalt; END; IF (fd = STDIN) THEN getcf := getc(c) ELSE IF fdeof(fd) THEN c := ENDFILE ELSE IF fdeoln(fd) THEN BEGIN fdreadln(fd); c := NEWLINE END ELSE BEGIN fdread(fd, ch); c := ord(ch) END; getcf := c END; { Copyright (C) 1981 by Bell Laboratories, Inc., and Whitesmiths Ltd. } { getline (UCB) -- get a line from file } FUNCTION getline (VAR s : string; fd : filedesc; maxsize : integer) : boolean; VAR i : integer; c : character; BEGIN i := 1; REPEAT s[i] := getcf(c, fd); i := i + 1 UNTIL (c = ENDFILE) OR (c = NEWLINE) OR (i >= maxsize); IF (c = ENDFILE) THEN { went one too far } i := i - 1; s[i] := ENDSTR; getline := (c <> ENDFILE) END; { putcf (UCB) -- put a single character on file fd } PROCEDURE putcf (c : character; fd : filedesc); BEGIN if (fd = lineout) then xmtchar(CHR(c)) ELSE IF c = NEWLINE THEN fdwriteln(fd) ELSE fdwrite(fd, chr(c)) END; { Copyright (C) 1981 by Bell Laboratories, Inc., and Whitesmiths Ltd. } { putstr (UCB) -- put out string on file } PROCEDURE putstr (VAR s : string; f : filedesc); VAR i : integer; BEGIN i := 1; WHILE (s[i] <> ENDSTR) DO BEGIN putcf(s[i], f); i := i + 1 END END; { open -- open a file for reading or writing } FUNCTION Sopen (VAR name : string; mode : integer) : filedesc; VAR i : integer; intname : mstring; found : boolean; BEGIN i := 1; WHILE (name[i] <> ENDSTR) AND (name[i] <> NEWLINE) AND (i <= FILENAMELENGTH) DO BEGIN if name[i] >= LETA then name[i] := name[i] - 32; { upper case } intname[i] := chr(name[i]); i := i + 1 END; FOR i := i TO FILENAMELENGTH DO intname[i] := ' '; { pad name with blanks } { find a free slot in openlist } Sopen := IOERROR; found := false; i := 1; WHILE (i <= MAXOPEN) AND (NOT found) DO BEGIN IF (filemode[i] = IOAVAIL) THEN BEGIN fdbind(i,intname); IF (BindStatus <> 0) AND (mode = IOWRITE) THEN BEGIN FILECREATE(intname); fdbind(i,intname); END; IF BindStatus = 0 THEN BEGIN filemode[i] := mode; IF (mode = IOREAD) THEN fdreset(i) ELSE fdrewrite(i); Sopen:=i; END ELSE Sopen := 0; found := true END; i := i + 1 END END; PROCEDURE Sclose (fd : filedesc); BEGIN IF (fd > STDOUT) AND (fd <= MAXOPEN) THEN BEGIN filemode[fd] := IOAVAIL; fdclose(fd); END END; { Copyright (C) 1981 by Bell Laboratories, Inc., and Whitesmiths Ltd. } { itoc - convert integer n to char string in s[i]... } FUNCTION itoc (n : integer; VAR s : string; i : integer) : integer; { returns end of s } BEGIN IF (n < 0) THEN BEGIN s[i] := ord('-'); itoc := itoc(-n, s, i+1) END ELSE BEGIN IF (n >= 10) THEN i := itoc(n DIV 10, s, i); s[i] := n MOD 10 + ord('0'); s[i+1] := ENDSTR; itoc := i + 1 END END; { Copyright (C) 1981 by Bell Laboratories, Inc., and Whitesmiths Ltd. } { length -- compute length of string } FUNCTION length (VAR s : string) : integer; VAR n : integer; BEGIN n := 1; WHILE (s[n] <> ENDSTR) DO n := n + 1; length := n - 1 END; { Copyright (C) 1981 by Bell Laboratories, Inc., and Whitesmiths Ltd. } { scopy -- copy string at src[i] to dest[j] } PROCEDURE scopy (VAR src : string; i : integer; VAR dest : string; j : integer); BEGIN WHILE (src[i] <> ENDSTR) DO BEGIN dest[j] := src[i]; i := i + 1; j := j + 1 END; dest[j] := ENDSTR END; { copyright (c) 1981 university of toronto computing services } { isupper -- true if c is upper case letter } { kludge version for omsi pascal } FUNCTION isupper (c : character) : boolean; BEGIN isupper := (c >= ord('A')) AND (c <= ord('Z')) END; { Copyright (C) 1981 by Bell Laboratories, Inc., and Whitesmiths Ltd. } { index -- find position of character c in string s } FUNCTION index (VAR s : string; c : character) : integer; VAR i : integer; BEGIN i := 1; WHILE (s[i] <> c) AND (s[i] <> ENDSTR) DO i := i + 1; IF (s[i] = ENDSTR) THEN index := 0 ELSE index := i END; FUNCTION getarg(n:integer;VAR s:string;maxsize:integer): BOOLEAN; (* return the nth argument *) BEGIN IF ((n<1) OR (cmdargs ENDSTR) AND (name[i] <> NEWLINE) AND (i <= FILENAMELENGTH) DO BEGIN intname[i] := chr(name[i]); i := i + 1 END; FOR i := i TO FILENAMELENGTH DO intname[i] := ' '; { pad name with blanks } bind(filen,intname,BindStatus); Exists := (BindStatus = 0); END; PROCEDURE PutNum((* Using *) n:integer; (* Using *) fd:filedesc); (* Ouput number *) VAR s: string; dummy: integer; BEGIN s[1] := BLANK; dummy := itoc(n,s,2); putstr(s,fd); END; PROCEDURE initcmd; (* read command line *) VAR idx : 1.. MAXSTR; i:integer; prom:cstring; dummy : boolean; BEGIN prom := 'KERMIT-H> '; (* Prompt *) PutCon(prom,STDOUT); dummy := getline(cmdlin,STDIN,MAXSTR); IF (cmdlin[1] <> ENDSTR) THEN FOR i:= 1 TO length(cmdlin) DO begin IF isupper(cmdlin[i]) THEN cmdlin[i]:=cmdlin[i] + 32; IF (cmdlin[i]=newline) then CMDLIN[I]:=ENDSTR; end; cmdargs := 0; (* initialize *) idx := 1; WHILE (cmdlin[idx]<>endstr) DO BEGIN WHILE (cmdlin[idx]=blank) DO idx := idx+1; IF (cmdlin[idx]<>endstr) THEN BEGIN cmdargs := cmdargs+1; cmdidx[cmdargs] := idx; WHILE (cmdlin[idx]<>endstr) AND (cmdlin[idx]<>BLANK) DO idx := idx+1; cmdlin[idx] := ENDSTR; idx := idx+1; END; END; END; PROCEDURE AddTo((* Updating *) VAR sum : Stats; (* Using *) inc:integer); (* This is used to avoid integer overflows without using 'reals' *) BEGIN sum[Low] := sum[Low] + inc; IF (sum[Low] >= 1000) THEN BEGIN sum[High] := sum[High] +1; sum[Low ] := sum[Low] - 1000; END; END; PROCEDURE OverHd((* Using *) p,f: Stats; (* Returning *) VAR o:integer); (* Calculate OverHead as % *) (* 0verHead := (p-f)*100/f *) BEGIN o:= 0; END; PROCEDURE CalRat((* Using *) f: Stats; (* Using *) t:integer; (* Returning *) VAR r:integer); (* Calculate Effective Baud Rate *) (* Rate = f*10/t *) BEGIN r := 0; END; FUNCTION UnChar((* Using *) c:character): (* Returning *) character; (* reverse of makechar *) BEGIN UnChar := c-BLANK END; PROCEDURE PutOut( p : Ppack); (* Output Packet *) VAR i : integer; BEGIN IF (Pad >0) THEN FOR i := 1 TO Pad DO putcf(PadChar,LineOut); WITH Buf[p] DO BEGIN putcf(mark,LineOut); putcf(count,LineOut); PutCon ( 'Sending Packet... ',STDout); PutNum(Unchar(seq),STDout); putcf(seq,LineOut); putcf(ptype,LineOut); putstr(data,LineOut); END; END; PROCEDURE StartTimer; BEGIN TimeLeft := TheirTimeOut; END; PROCEDURE StopTimer; BEGIN TimeLeft := MaxInt; END; FUNCTION MakeChar((* Using *) c:character): (* Returning *) character; (* convert integer to printable *) BEGIN MakeChar := c+BLANK; END; FUNCTION IsControl((* Using *) c:character): (* Returning *) boolean; (* true if control *) BEGIN IsControl := (c=DEL ) OR (c < BLANK ); END; FUNCTION IsPrintable((* Using *) c:character): (* Returning *) boolean; (* opposite of iscontrol *) BEGIN IsPrintable := NOT IsControl(c); END; FUNCTION Ctl((* Using *) c:character): (* Returning *) character; (* c XOR 100 *) BEGIN IF IsControl(c) THEN c := c+64 ELSE c := c-64; Ctl := c; END; FUNCTION IsValidPType((* Using *) c:character): (* Returning *) boolean; (* true if valid packet type *) BEGIN IsValidPType := (c =TYPEB) OR (c=TYPED) OR (c=TYPEE) OR (c=TYPEF) OR (c=TYPEN) OR (c=TYPES) OR (c=TYPET) OR (c=TYPEY) OR (c=TYPEZ) END; FUNCTION CheckFunction((* Using *) c:integer): (* Returning *) character; (* calculate checksum *) VAR x: integer; BEGIN (* CheckFunction := (c + ( c AND 300 ) /100 ) AND 77; *) x := (c MOD 256 ) DIV 64; x := x+c; CheckFunction := x MOD 64; END; PROCEDURE EnCodeParm((* Updating *) VAR data:string); (* encode parameters *) VAR i: integer; BEGIN FOR i:=1 TO NUMPARAM DO data[i] := BLANK; data[NUMPARAM+1] := ENDSTR; data[1] := MakeChar(SizeRecv); (* my biggest packet *) data[2] := MakeChar(MyTimeOut); (* when I want timeout*) data[3] := MakeChar(MyPad); (* how much padding *) data[4] := Ctl(MyPadChar); (* my padding character *) data[5] := MakeChar(myEOL); (* my EOL *) data[6] := MyQuote; (* my quote char *) END; PROCEDURE DeCodeParm((* Using *) VAR data:string); (* decode parameters *) BEGIN SizeSend := UnChar(data[1]); TheirTimeOut := UnChar(data[2]); (* when I should time out *) Pad := UnChar(data[3]); (* padding characters to send *) PadChar := Ctl(data[4]); (* padding character *) SendEOL := UnChar(data[5]); (* EOL to send *) SendQuote := data[6]; (* quote to send *) END; PROCEDURE ReadParm ((* Updating *) VAR Parms:string); VAR dummy : boolean; fd : filedesc; BEGIN; (* read parameters *) Parms[1]:=ENDSTR; IF Exists(ParmFile) THEN BEGIN fd := Sopen(ParmFile,IOREAD); dummy := getline(Parms,fd,MAXSTR); Sclose(fd); END; END; PROCEDURE GetParm; (* get parameters from file *) VAR data:string; BEGIN; ReadParm(data); IF (length(data) > 0) THEN (* get parameters *) BEGIN SizeRecv := UnChar(data[1]); MyTimeOut := UnChar(data[2]); (* when I should time out *) MyPad := UnChar(data[3]); (* padding characters to send *) MyPadChar := Ctl(data[4]); (* padding character *) MyEOL := UnChar(data[5]); (* EOL to send *) MyQuote := data[6]; (* quote to send *) END; END; PROCEDURE SYSarguments; (* process special arguments for SYSTEM *) BEGIN (* nothing *) END; PROCEDURE StartRun; (* initialization as necessary *) BEGIN RunTime := 0; END; PROCEDURE Usage; (* Print writeln & exit *) BEGIN writeln; writeln( 'usage: KERMIT-H> [Help] [Connect] [Send/Receive/Print]'); END; PROCEDURE SetParameters; (* set new Parameter File Name *) BEGIN IF (length(aline) > 2) THEN BEGIN scopy(aline,3,ParmFile,1); GetParm; (* read new parameters *) END; END; PROCEDURE KermitInit; (* initialize various parameters & defaults *) BEGIN n := 0; NumSendPacks := 0; NumRecvPacks := 0; NumACK := 0; NumNAK := 0; NumACKrecv := 0; NumNAKrecv := 0; NumBADrecv := 0; ChInFile[Low] := 0; ChInFile[High] := 0; ChInPack := ChInFile; OneWayOnly := false; Verbosity := false; (* default to false *) Trace := false; (* default to no trace *) Debug := false; RunType := invalid; DiskFile := IOERROR; (* to indicate not open yet *) ThisPacket := 1; LastPacket := 2; CurrentPacket := 3; NextPacket := 4; InputPacket := 5; WITH TOPacket DO BEGIN count := 3; seq := 0; ptype := TYPEN; data[1] := ENDSTR; END; NextArg := 1; (* get first argument *) IF (NextArg<=nargs) THEN IF NOT getarg(NextArg,aline,MAXSTR) THEN Usage; FROMCONSOLE:=NOTHING; END; PROCEDURE FinishUp; (* do any End of Program clean up *) VAR overhead ,effrate : integer; BEGIN Sclose(DiskFile); (* print info on number of packets etc *) IF ((RunType <> Invalid) AND Local ) THEN BEGIN PutCon('Packets sent: ',STDOUT); PutNum(NumSendPacks,STDOUT); PutCon('Packets received ',STDOUT); PutNum(NumRecvPacks,STDOUT); (* Calculate overhead *) OverHd(ChInPack,ChInFile,overhead); IF (Overhead <>0) THEN BEGIN PutCon('Overhead (%): ' ,STDOUT); PutNum(overhead,STDOUT); END; IF (RunTime <> 0) THEN BEGIN (* calculate effective rate *) CalRat(ChInFile,RunTime,effrate); PutCon('Effective Rate: ',STDOUT); PutNum(effrate,STDOUT); END; IF (RunType = Transmit) THEN BEGIN PutCon('Number of ACK: ',STDOUT); PutNum(NumACKrecv,STDOUT); PutCon('Number of NAK: ',STDOUT); PutNum(NumNAKrecv,STDOUT); PutCon('Number of BAD: ',STDOUT); PutNum(NumBADrecv,STDOUT); END ELSE BEGIN (* for Receive *) PutCon('Number of ACK: ',STDOUT); PutNum(NumACK,STDOUT); PutCon('Number of NAK: ',STDOUT); PutNum(NumNAK,STDOUT); END; putcf(NEWLINE,STDOUT); END; State := Abort; Local := false; END; PROCEDURE DebugPacket((* Using *) mes : cstring; (* Using *) VAR p : Ppack); (* Print Debugging Info *) BEGIN PutCon(mes,STDOUT); WITH Buf[p] DO BEGIN PutNum(Unchar(count),STDOUT); PutNum(Unchar(seq),STDOUT); putcf(BLANK,STDOUT); putcf(ptype,STDOUT); putcf(NEWLINE,STDOUT); putstr(data,STDOUT); putcf(NEWLINE,STDOUT); END; END; PROCEDURE ReSendPacket; (* re -sends previous packet *) BEGIN NumSendPacks := NumSendPacks+1; AddTo(ChInPack,Pad + UnChar(Buf[LastPacket].count) + 3); IF Debug THEN DebugPacket('Re-Sending ... ',LastPacket); PutOut(LastPacket); END; PROCEDURE SendPacket; (* expects count as length of data portion *) (* and seq as number of packet *) (* builds & sends packet *) VAR i,len,chksum : integer; temp : Ppack; BEGIN IF (NumTry <> 1) AND (RunType = Transmit ) THEN ReSendPacket ELSE BEGIN WITH Buf[ThisPacket] DO BEGIN mark :=SOH; (* mark *) len := count; (* save length *) count := MakeChar(len+3); (* count = 3+length of data *) seq := MakeChar(seq); (* seq number *) chksum := count + seq + ptype; IF ( len > 0) THEN (* is there data ? *) FOR i:= 1 TO len DO chksum := chksum + data[i]; (* loop for data *) chksum := CheckFunction(chksum); (* calculate checksum *) data[len+1] := MakeChar(chksum); (* make printable & output *) data[len+2] := SendEOL; (* EOL *) data[len+3] := ENDSTR; END; NumSendPacks := NumSendPacks+1; IF Debug THEN DebugPacket('Sending ... ',ThisPacket); PutOut(ThisPacket); IF RunType = Transmit THEN BEGIN AddTo(ChInPack,Pad + len + 6); temp := LastPacket; LastPacket := ThisPacket; ThisPacket := temp; END; END END; PROCEDURE SendACK((* Using *) n:integer); (* send ACK packet *) BEGIN WITH Buf[ThisPacket] DO BEGIN count := 0; seq := n; ptype := TYPEY; END; SendPacket; NumACK := NumACK+1; END; PROCEDURE SendNAK((* Using *) n:integer); (* send NAK packet *) BEGIN WITH Buf[ThisPacket] DO BEGIN count := 0; seq := n; ptype := TYPEN; END; SendPacket; NumNAK := NumNAK+1; END; PROCEDURE ErrorPack((* Using *) c:cstring); (* output Error packet if necessary -- then exit *) BEGIN IF (TTYmode = Cooked) THEN PutCon(c,STDOUT) ELSE BEGIN WITH Buf[ThisPacket] DO BEGIN seq := n; ptype := TYPEE; CtoS(c,data); count := length(data); END; SendPacket; END; FinishUp; State := Abort; END; PROCEDURE Verbose((* Using *) c:cstring); (* Print writeln if verbosity *) BEGIN IF Verbosity THEN PutCon(c,STDOUT); END; PROCEDURE PutErr((* Using *) c:cstring); (* Print error_messages *) BEGIN PutCon(c,STDOUT); IF (TtyMode = Raw) AND (NumKeptErrors < MAXERRORS) THEN BEGIN NumKeptErrors := NumKeptErrors + 1; KeptErrors[NumKeptErrors] := c; END; END; PROCEDURE Field1; (* Count *) VAR test: boolean; BEGIN WITH Buf[NextPacket] DO BEGIN WITH PackControl DO BEGIN Buf[InputPacket].count := t; count := UnChar(t); test := (count >= 3) OR (count <= SizeRecv-2); IF NOT test THEN Verbose('Bad count '); good := good AND test; END; END; END; PROCEDURE Field2; (* Packet Number *) VAR test : boolean; BEGIN WITH Buf[NextPacket] DO BEGIN WITH PackControl DO BEGIN Buf[InputPacket].seq := t; seq := UnChar(t); test := (seq >= 0) OR (seq <= 63); IF NOT test THEN Verbose('Bad seq number '); good := test AND good; END; END; END; PROCEDURE Field3; (* Packet Type *) VAR test : boolean; BEGIN WITH Buf[NextPacket] DO BEGIN WITH PackControl DO BEGIN ptype := t; Buf[InputPacket].ptype := t; test := IsValidPType(ptype); IF NOT test THEN Verbose('Bad Packet Type '); good := test AND good; END; END; END; PROCEDURE Field4; (* Data *) BEGIN WITH PackControl DO BEGIN PacketPtr := PacketPtr+1; Buf[InputPacket].data[PacketPtr] := t; WITH Buf[NextPacket] DO BEGIN IF (t=MyQuote) AND (ptype <> TYPEY) AND (ptype <> TYPES) THEN (* character is quote *) BEGIN IF control THEN (* quote ,quote *) BEGIN data[i] := MyQuote; i := i+1; control := false; END ELSE (* set control on *) control := true END ELSE (* not quote *) IF control THEN (* convert to control *) BEGIN data[i] := ctl(t); i := i+1; control := false END ELSE (* regular data *) BEGIN data[i] := t; i := i+1; END; END; END; END; PROCEDURE Field5; (* Check Sum *) VAR test : boolean; BEGIN WITH PackControl DO BEGIN PacketPtr := PacketPtr +1; Buf[InputPacket].data[PacketPtr] := t; Buf[InputPacket].data[PacketPtr + 1] := ENDSTR; check := CheckFunction(check); check := MakeChar(check); test := (t=check); IF NOT test THEN Verbose('Bad CheckSum '); good := test AND good; Buf[NextPacket].data[i] := ENDSTR; finished := true; (* set finished *) END; END; PROCEDURE BuildPacket; (* receive packet & validate checksum *) VAR temp : Ppack; BEGIN WITH PackControl DO BEGIN WITH Buf[NextPacket] DO BEGIN IF (t<>ENDSTR) THEN IF restart THEN BEGIN (* read until get SOH marker *) IF (t = SOH) THEN BEGIN finished := false; (* set varibles *) control := false; good := true; seq := -1; (* set return values to bad packet *) ptype := QUESTION; data[1] := ENDSTR; data[MAXSTR] := ENDSTR; restart := false; fld := 0; i := 1; PacketPtr := 0; check := 0; END; END ELSE (* have started packet *) BEGIN IF (t=SOH) (* check for restart or EOL *) THEN restart := true ELSE IF (t=myEOL) THEN BEGIN finished := true; good := false; END ELSE BEGIN CASE fld OF (* increment field number *) 0: fld := 1; 1: fld := 2; 2: fld := 3; 3: IF (count=3) (* no data *) THEN fld := 5 ELSE fld := 4; 4: IF (PacketPtr>=count-3) (* end of data *) THEN fld := 5; END (* case *); IF (fld<>5) THEN check := check+t; (* add into checksum *) CASE fld OF 1: Field1; 2: Field2; 3: Field3; 4: Field4; 5: Field5; END; (* case *) END; END; IF finished THEN BEGIN IF (ptype=TYPEE) AND good THEN (* error_packets *) BEGIN putstr(data,STDOUT); FinishUp; SendACK(n); (* send ACK *) END; NumRecvPacks := NumRecvPacks+1; IF Debug THEN BEGIN DebugPacket('Received ... ',InputPacket); IF good THEN PutCon('Is Good ',STDOUT); END; temp := CurrentPacket; CurrentPacket := NextPacket; NextPacket := temp; END; END; END; END; FUNCTION ReceivePacket: boolean; BEGIN WITH PackControl DO BEGIN StartTimer; IF (Runtype = Receive) AND (State = Init) THEN TimeLeft := 10 * TimeLeft; { Long wait for first message } finished := false; restart := true; good := false; FromConsole := nothing; (* No Interupt *) REPEAT t := GetIn(TimeLeft,FromConsole); IF Local (* check Interupt *) THEN BEGIN CASE FromConsole OF abortnow: BEGIN FinishUp; STIPHALT; END; nothing: (* nothing *); CRin: BEGIN t := MyEOL; FromConsole := nothing; END; END; end; (* case *) BuildPacket; UNTIL finished OR (TimeLeft = 0); IF (TimeLeft = 0) THEN BEGIN Buf[CurrentPacket] := TOPacket; restart := true; IF NOT ((RunType=Transmit) AND (State=Init)) THEN BEGIN PutCon('Timed Out ',STDOUT); END; END; StopTimer; ReceivePacket := good; END; END; FUNCTION ReceiveACK : (* Returning *) boolean; (* receive ACK with correct number *) VAR Ok: boolean; BEGIN IF (NOT OneWayOnly ) THEN Ok := ReceivePacket; WITH Buf[CurrentPacket] DO BEGIN IF (ptype=TYPEY) THEN NumACKrecv := NumACKrecv+1 ELSE IF (ptype=TYPEN) THEN NumNAKrecv := NumNAKrecv+1 ELSE IF NOT OneWayOnly THEN NumBadrecv := NumBadrecv +1; (* got right one ? *) ReceiveACK := ( Ok AND (ptype=TYPEY) AND (n=seq)) OR ( OneWayOnly) END; END; PROCEDURE GetData((* Returning *) VAR newstate:KermitStates); (* get data from file into ThisPacket *) VAR (* and return next state - data & EOF *) x,c : character; i: integer; BEGIN IF (NumTry=1) THEN BEGIN i := 1; x := ENDSTR; WITH Buf[ThisPacket] DO BEGIN WHILE (i< SizeSend - 8 ) AND (x <> ENDFILE) (* leave room for quote & NEWLINE *) DO BEGIN x := getcf(c,DiskFile); IF (x<>ENDFILE) THEN IF (IsControl(x)) OR (x=SendQuote) THEN BEGIN (* control char -- quote *) IF (x=NEWLINE) THEN (* use proper EOL *) CASE EOLforFile OF LineFeed: (* ok as is *); CrLf: BEGIN data[i] := SendQuote; i := i+1; data[i] := Ctl(CR); i := i+1; (* LF will sent below *) END; JustCR: x := CR; END (* case *); data[i] := SendQuote; i := i+1; IF (x<>SendQuote) THEN data[i] := Ctl(x) ELSE data[i] := SendQuote; END ELSE (* regular char *) data[i] := x; IF (x<>ENDFILE) THEN BEGIN i := i+1; (* increase count for next char *) AddTo(ChInFile,1); END; END; data[i] := ENDSTR; (* to terminate string *) count := i -1; (* length *) seq := n; ptype := TYPED; IF (x=ENDFILE) THEN BEGIN newstate := EOFile; Sclose(DiskFile); DiskFile := ioerror; END ELSE newstate := FileData; SaveState := newstate; (* save state *) END END ELSE newstate := SaveState; (* get old state *) END; FUNCTION GetNextFile: (* Returning *) boolean; (* get next file to send in ThisPacket *) (* returns true if no more *) VAR result: boolean; BEGIN result := true; IF (NumTry=1) THEN WITH Buf[ThisPacket] DO BEGIN REPEAT IF getarg(NextArg,data,MAXSTR) THEN BEGIN (* open file *) IF Exists(data) THEN BEGIN DiskFile := Sopen(data,IOREAD); count := length(data); AddTo(ChInFile , count); seq := n; ptype := TYPEF; PutCon(' SENDING... ',STDOUT); putstr(data,stdout); IF DiskFile <= IOERROR THEN ErrorPack('Cannot open file '); result := false; FIXNAME(data); END; END; NextArg := NextArg+1; UNTIL ( NextArg > nargs ) OR ( NOT result ) END ELSE result := false; (* for saved packet *) GetNextFile := result; END; PROCEDURE SendFile; (* send file name packet *) BEGIN Verbose( 'Sending .... '); IF NumTry > MaxTry THEN BEGIN PutErr ('Send file - Too Many'); State := Abort; (* too many tries, abort *) END ELSE BEGIN NumTry := NumTry+1; IF GetNextFile THEN BEGIN State := Break; NumTry := 0; END ELSE BEGIN IF Verbosity THEN IF (NumTry = 1) THEN putstr(Buf[ThisPacket].data,STDOUT) ELSE putstr(Buf[LastPacket].data,STDOUT); SendPacket; (* send this packet *) IF ReceiveACK THEN BEGIN State := FileData; NumTry := 0; n := (n+1) MOD 64; END END; END; END; PROCEDURE SendData; (* send file data packets *) VAR newstate: KermitStates; BEGIN IF Verbosity THEN BEGIN PutCon ( 'Sending data ',STDOUT); PutNum(n,STDOUT); END; IF NumTry > MaxTry THEN BEGIN State := Abort; (* too many tries, abort *) PutErr ('Send data - Too many'); END ELSE BEGIN NumTry := NumTry+1; GetData(newstate); SendPacket; IF ReceiveACK THEN BEGIN State := newstate; NumTry := 0; n := (n+1) MOD 64; END END; END; PROCEDURE SendEOF; (* send EOF packet *) BEGIN Verbose ('Sending EOF '); IF NumTry > MaxTry THEN BEGIN State := Abort; (* too many tries, abort *) PutErr('Send EOF - Too Many '); END ELSE BEGIN NumTry := NumTry+1; IF (NumTry = 1) THEN BEGIN WITH Buf[ThisPacket] DO BEGIN ptype := TYPEZ; seq := n; count := 0; END END; SendPacket; IF ReceiveACK THEN BEGIN State := FileHeader; NumTry := 0; n := (n+1) MOD 64; END END; END; PROCEDURE SendBreak; (* send break packet *) BEGIN Verbose ('Sending break '); IF NumTry > MaxTry THEN BEGIN State := Abort; (* too many tries, abort *) PutErr('Send break -Too Many'); END ELSE BEGIN NumTry := NumTry+1; (* make up packet *) IF NumTry = 1 THEN BEGIN WITH Buf[ThisPacket] DO BEGIN ptype := TYPEB; seq := n; count := 0; END END; SendPacket; (* send this packet *) IF ReceiveACK THEN BEGIN State := Complete; END END; END; PROCEDURE SendInit; (* send init packet *) BEGIN Verbose ('Sending init '); IF NumTry > MaxTry THEN BEGIN State := Abort; (* too many tries, abort *) PutErr('Cannot Initialize '); END ELSE BEGIN NumTry := NumTry+1; IF (NumTry = 1) THEN BEGIN WITH Buf[ThisPacket] DO BEGIN EnCodeParm(data); count := NUMPARAM; seq := n; ptype := TYPES; END END; SendPacket; (* send this packet *) IF ReceiveACK THEN BEGIN WITH Buf[CurrentPacket] DO BEGIN IF OneWayOnly THEN (* use same data if test mode *) data := Buf[LastPacket].data; SizeSend := UnChar(data[1]); TheirTimeOut := UnChar(data[2]); Pad := UnChar(data[3]); PadChar := Ctl(data[4]); SendEOL := CR; (* default to CR *) IF (length(data) >= 5) THEN IF (data[5] <> 0) THEN SendEOL := UnChar(data[5]); SendQuote := SHARP; (* default # *) IF (length(data) >= 6) THEN IF (data[6] <> 0) THEN SendQuote := data[6]; END; State := FileHeader; NumTry := 0; n := (n+1) MOD 64; END; END; END; PROCEDURE SendSwitch; (* Send-switch is the state table switcher for sending files. * It loops until either it is finished or a fault is encountered. * Routines called by sendswitch are responsible for changing the state. *) BEGIN State := Init; (* send initiate is the start state *) NumTry := 0; (* say no tries yet *) IF NOT Local THEN BEGIN TTYRAW; (* if host--put tty in raw mode *) TtyMode := Raw; END; IF (NOT OneWayOnly ) THEN Sleep(Delay); StartRun; REPEAT CASE State OF FileData: SendData; (* data-send state *) FileHeader: SendFile; (* send file name *) EOFile: SendEOF; (* send end-of-file *) Init: SendInit; (* send initialize *) Break: SendBreak; (* send break *) Complete: (* nothing *); Abort: (* nothing *); END (* case *); UNTIL ( (State = Abort) OR (State=Complete) ); FLUSH; (* flush output buffer *) IF TtyMode = Raw THEN BEGIN TTYCOOKED; (* if host--return tty to cooked mode *) TtyMode := Cooked; END; END; PROCEDURE GetFile((* Using *) data:string); (* create file from fileheader packet *) VAR strend: integer; BEGIN putstr(aline,stdout); IF (RUNTYPE=PRINT) THEN DiskFile := Sopen(aline,IOWRITE) ELSE WITH Buf[CurrentPacket] DO BEGIN IF DiskFile = IOERROR (* check if we already have a file *) THEN BEGIN IF Verbosity THEN BEGIN PutCon ('Creating file ... ',STDOUT); putstr(data,STDOUT); END; (* check position of '.' -- truncate if bad *) IF (index(data,PERIOD) > FLEN1 ) THEN BEGIN data[FLEN1] := PERIOD; data[FLEN1 + 1] := ENDSTR; END; (* check Max length *) IF length(data) > FLEN2 THEN data[FLEN2 +1] := ENDSTR; IF Exists(data) THEN BEGIN PutCon('File already exists ',STDOUT); putstr(data,STDOUT); PutCon('Creating ... ',STDOUT); CtoS(TEMPFILE,data); strend := 0; REPEAT strend := strend +1; UNTIL (data[strend] = BLANK); strend := itoc(n,data,strend); putstr(data,STDOUT); END; DiskFile := Sopen(data,IOWRITE); END; IF (Diskfile <= IOERROR) THEN ErrorPack('Cannot create file '); END; END; PROCEDURE ReceiveInit; (* receive init packet *) (* respond with ACK and our parameters *) BEGIN IF NumTry > MaxTry THEN BEGIN State := Abort; PutErr('Cannot receive init '); END ELSE BEGIN Verbose ( 'Receiving Init '); NumTry := NumTry+1; IF ReceivePacket AND (Buf[CurrentPacket].ptype = TYPES) THEN BEGIN WITH Buf[CurrentPacket] DO BEGIN n := seq; DeCodeParm(data); END; (* now send mine *) WITH Buf[ThisPacket] DO BEGIN count := NUMPARAM; seq := n; Ptype := TYPEY; EnCodeParm(data); END; SendPacket; NumACK := NumACK+1; State := FileHeader; OldTry := NumTry; NumTry := 0; n := (n+1) MOD 64 END ELSE BEGIN IF Debug THEN PutCon('Received Bad init ',STDOUT); SendNAK(n); END; END; END; PROCEDURE DataToFile; (* output to file *) VAR len,i : integer; temp : string; BEGIN WITH Buf[CurrentPacket] DO BEGIN len := length(data); AddTo(ChInFile ,len); CASE EOLforFile OF LineFeed: putstr(data,DiskFile); CrLf: BEGIN (* don't output CR *) FOR i:=1 TO len DO IF data[i] <> CR THEN putcf(data[i],DiskFile); END; JustCR: BEGIN (* change CR to NEWLINE *) FOR i:=1 TO len DO IF data[i]=CR THEN data[i]:=NEWLINE; putstr(data,DiskFile); END; END; (* case *) END; END; PROCEDURE Dodata; (* Process Data packet *) BEGIN WITH Buf[CurrentPacket] DO BEGIN IF seq = ((n + 63) MOD 64) THEN BEGIN (* data last one *) IF OldTry>MaxTry (* number of tries? *) THEN BEGIN State := Abort; PutErr('Old data - Too many '); END ELSE BEGIN SendACK(seq); NumTry := 0; END; END ELSE BEGIN (* data - this one *) IF (n<>seq) THEN SendNAK(n) ELSE BEGIN SendACK(n); (* ACK *) DataToFile; OldTry := NumTry; NumTry := 0; n := (n+1) MOD 64; END; END; END; END; PROCEDURE DoFileLast; (* Process File Packet *) BEGIN (* File header - last one *) IF OldTry > MaxTry (* tries ? *) THEN BEGIN State := Abort; PutErr('Old file - Too many '); END ELSE BEGIN OldTry := OldTry+1; WITH Buf[CurrentPacket] DO BEGIN IF seq = ((n + 63) MOD 64) (* packet number *) THEN BEGIN (* send ACK *) SendACK(seq); NumTry := 0 END ELSE BEGIN SendNAK(n); (* NAK *) END; END; END; END; PROCEDURE DoEOF; (* Process EOF packet *) BEGIN (* EOF - this one *) IF Buf[CurrentPacket].seq<>n (* packet number ? *) THEN SendNAK(n) (* NAK *) ELSE BEGIN (* send ACK *) SendACK(n); Sclose(DiskFile); (* close file *) DiskFile := IOERROR; OldTry := NumTry; NumTry := 0; n := (n+1) MOD 64; (* next packet *) State := FileHeader; (* change state *) END; END; PROCEDURE ReceiveData; (* Receive data packets *) VAR strend: integer; packetnum: string; good : boolean; BEGIN IF NumTry > MaxTry (* check number of tries *) THEN BEGIN State := Abort; CtoS('Recv data -Too many ',packetnum); strend := itoc(n,packetnum,CONLENGTH+1); putstr(packetnum,STDOUT); END ELSE BEGIN NumTry := NumTry+1; (* increase number of tries *) good := ReceivePacket; (* get packet *) WITH Buf[CurrentPacket] DO BEGIN IF Verbosity THEN BEGIN PutCon('Receiving (Data) ',STDOUT); PutNum(Buf[CurrentPacket].seq,STDOUT); END; IF ((ptype = TYPED) OR (ptype=TYPEZ) OR (ptype=TYPEF)) AND good (* check type *) THEN CASE ptype OF TYPED: DoData; TYPEF: DoFileLast; TYPEZ: DoEOF; END (* case *) ELSE BEGIN Verbose('Expected data pack '); SendNAK(n); END; END; END; END; PROCEDURE DoBreak; (* Process Break packet *) BEGIN (* Break transmission *) IF Buf[CurrentPacket].seq<>n (* packet number ? *) THEN SendNAK(n) (* NAK *) ELSE BEGIN (* send ACK *) SendACK(n) ; State := Complete (* change state *) END END; PROCEDURE DoFile; (* Process file packet *) BEGIN (* File Header *) WITH Buf[CurrentPacket] DO BEGIN IF seq<>n (* packet number ? *) THEN SendNAK(n) (* NAK *) ELSE BEGIN (* send ACK *) SendACK(n); AddTo(ChInFile, length(data)); GetFile(data); (* get file name *) OldTry := NumTry; NumTry := 0; n := (n+1) MOD 64; (* next packet *) IF (State <> Abort) THEN State := FileData; (* change state *) END; END; END; PROCEDURE DoEOFLast; (* Process EOF Packet *) BEGIN (* End Of File Last One*) IF OldTry > MaxTry (* tries ? *) THEN BEGIN State := Abort; PutErr('Old EOF - Too many '); END ELSE BEGIN OldTry := OldTry+1; WITH Buf[CurrentPacket] DO BEGIN IF seq =((n + 63 ) MOD 64) (* packet number *) THEN BEGIN (* send ACK *) SendACK(seq); Numtry := 0 END ELSE BEGIN SendNAK(n); (* NAK *) END END; END; END; PROCEDURE DoInitLast; BEGIN (* Init Packet - last one *) IF OldTry>MaxTry (* number of tries? *) THEN BEGIN State := Abort; PutErr('Old init - Too many '); END ELSE BEGIN OldTry := OldTry+1; IF Buf[CurrentPacket].seq = ((n + 63) MOD 64) (* packet number *) THEN BEGIN (* send ACK *) WITH Buf[ThisPacket] DO BEGIN count := NUMPARAM; seq := Buf[CurrentPacket].seq; ptype := TYPEY; EnCodeParm(data); END; SendPacket; NumACK := NumACK+1; NumTry := 0; END ELSE BEGIN SendNAK(n); (* NAK *) END; END; END; PROCEDURE ReceiveFile; (* receive file packet *) VAR good: boolean; BEGIN IF NumTry > MaxTry (* check number of tries *) THEN BEGIN State := Abort; PutErr('Recv file - Too many'); END ELSE BEGIN NumTry := NumTry+1; (* increase number of tries *) good := ReceivePacket; (* get packet *) WITH Buf[CurrentPacket] DO BEGIN IF VERBOSITY THEN BEGIN PutCon('Receiving (File) ',STDOUT); PutNum(seq,STDOUT); END; PutCon(' RECEIVING... ',STDOUT); putstr(data,stdout); IF ((ptype = TYPES) OR (ptype=TYPEZ) OR (ptype=TYPEF) OR (ptype=TYPEB)) (* check type *) AND good THEN CASE ptype OF TYPES: DoInitLast; TYPEZ: DoEOFLast; TYPEF: DoFile; TYPEB: DoBreak; END (* case *) ELSE BEGIN IF Debug THEN PutCon('Expected File Pack ',STDOUT); SendNAK(n); END; END; END; END; PROCEDURE RecvSwitch; (* this procedure is the main receive routine *) BEGIN State := Init; NumTry := 0; IF NOT Local THEN BEGIN TTYRAW; (* if host--put tty in raw mode *) TtyMode := Raw; END; StartRun; REPEAT CASE State OF FileData: ReceiveData; Init: ReceiveInit; Break: (* nothing *); FileHeader: ReceiveFile; EOFile: (* nothing *); Complete: (* nothing *); Abort: (* nothing *); END; (* case *) UNTIL (State = Abort ) OR ( State = Complete ); FLUSH; (* flush output buffer *) IF TtyMode = Raw THEN BEGIN TTYCOOKED; (* if host--return tty to cooked mode *) TtyMode := Cooked; END; END; BEGIN SYSinit; (* system dependent *) initio; done:=false; NumTry:=0; NumKeptErrors := 0; Pad := DEFPAD; (* set defaults *) MyPad := DEFPAD; PadChar := DEFPADCHAR; MyPadChar := DEFPADCHAR; TheirTimeOut := DEFTIMEOUT; MyTimeOut := DEFTIMEOUT; Delay := DEFDELAY; SizeRecv := MAXPACK; SizeSend := MAXPACK; SendEOL := DEFEOL; MyEOL := DEFEOL; SendQuote := DEFQUOTE; MyQuote := DEFQUOTE; MaxTry := DEFTRY; Halfduplex := DEFDUPLEX; CASE DEFEOLTYPE OF 1: EOLforFile := LineFeed; 2: EOLforFile := CrLf; 3: EOLforFile := JustCR; END (* case *); CtoS(PFILE,ParmFile); GetParm; Local := false; (* default to remote *) TtyMode := Cooked; repeat initcmd; KermitInit; (* initialize *) WHILE ( NextArg <= nargs ) AND (RUNTYPE<>transmit) and (RUNTYPE<>receive) and (RUNTYPE<>print) and (not done) DO BEGIN (* check for valid commands *) (* r s c M x u z *) IF (aline[1]=LETS) OR (aline[1]=LETR) OR (aline[1]=LETP) OR (aline[1]=LETC) OR (aline[1]=LETM) OR (aline[1]=LETX) OR (aline[1]=LETU) OR (aline[1]=LETZ) OR (aline[1]=LETH) OR (aline[1]=LETQ) OR (aline[1]=LETT) OR (aline[1]=LETE) THEN CASE aline[1] OF LETS: RunType := Transmit; LETR: RunType := Receive; LETP: RunType := PRINT; LETE,LETQ: done:=true; LETC: BEGIN (* look for -lvd *) FOR j := length(aline) DOWNTO 1 DO BEGIN IF (aline[j]=LETC) THEN BEGIN Local := true; IF NOT OneWayOnly THEN BEGIN Local := connect(Halfduplex); IF NOT Local THEN PutErr('Cannot connect '); END; END; IF (aline[j]=LETV) THEN Verbosity := true; IF (aline[j]=LETD) THEN Debug := true; IF (aline[j]=LETH) THEN Halfduplex := true; IF (aline[j]=LETF) THEN Halfduplex := false; END; END; LETH: BEGIN WRITELN; WRITELN('KERMIT-H Comands:'); WRITELN; (* WRITELN('C [H/F/D/V] - Connect [Half/Full duplex,Debug,Verbose]'); *) Writeln('S {} - Send files'); Writeln('R {} - Receive files'); (* Writeln('P {] - Print files'); *) Writeln('H - Help {this message}'); Writeln('E - Exit'); Writeln('Q - Quit'); END; LETX: OneWayOnly := true; LETM: SetParameters; LETU: SYSarguments; (* do special for SYSTEM *) LETZ: BEGIN IF (aline[2]=LETL) OR (aline[2]=LETC) OR (aline[2]=LETR) THEN CASE aline[2] OF LETL: EOLforFile := LineFeed; LETC: EOLforFile := CrLf; LETR: EOLforFile := JustCR; END (* case *); END; LETT: BEGIN FILECREATE(TRACEFILE); bind(filet,TRACEFILE,BindStatus); IF BindStatus = 0 THEN Trace := true; Verbosity := true; Debug := true; TtyMode := RAW; PutCon('Kermit Trace Output ',STDOUT); PutCon(' ',STDOUT); TtyMode := COOKED; END; END (* case *) ELSE Usage; (* get next argument *) NextArg := NextArg+1; IF (NextArg <= nargs ) THEN IF NOT getarg(NextArg,aline,MAXSTR) THEN Usage; END; CASE RunType OF Receive: BEGIN (* filename is optional here *) IF getarg(NextArg,aline,MAXSTR) THEN BEGIN IF Exists(aline) THEN BEGIN PutErr('Overwriting '); putstr(aline,STDOUT); END; DiskFile := Sopen(aline,IOWRITE); IF DiskFile <= IOERROR THEN ErrorPack('Cannot Open File '); END; RecvSwitch; END; PRINT: BEGIN CtoS(LP,aline); DiskFile := Sopen(aline,IOWRITE); IF DiskFile <= IOERROR THEN ErrorPack('Cannot Open File '); RecvSwitch; END; Transmit: BEGIN (* must give filename *) FOR j:= NextArg TO nargs DO BEGIN IF NOT getarg(NextArg,aline,MAXSTR) THEN Usage; IF NOT Exists(aline) THEN ErrorPack('File not found '); END; IF getarg(NextArg,aline,MAXSTR) THEN SendSwitch; END; Invalid: (* nothing *); SetParm: (* nothing *); END; (* case *) until done; FinishUp; (* End of Program *) IF (NumKeptErrors > 0) (* Print any message we couldn't before *) THEN BEGIN PutCon(' Delayed Messages:',STDOUT); FOR J := 1 TO NumKeptErrors DO PutCon(KeptErrors[J],STDOUT); END; 9999: SYSFINISH; (* do System dependent *) END.