home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / d / univac.pas < prev    next >
Pascal/Delphi Source File  |  2020-01-01  |  31KB  |  1,278 lines

  1. Date: Thu, 04 Oct 84 14:51:26 EDT   
  2. From: Edgar B. Butt <BUTT@UMD2.ARPA>
  3. To: sy.fdc@cu20b
  4. Subject: Oh no, another Kermit!     
  5.  
  6. Here is a Kermit implementation for the Sperry 1100 systems written 
  7. in Pascal.  It has been run successfully here at the University of Maryland,
  8. College Park, and at SUNY, Albany.  Please add it to your selection 
  9. of Kermits.  I would appreciate feedback from anyone who tries it.  
  10.  
  11. The first page of code consists of comments explaining how to   
  12. use and generate Kermit1100.
  13.  
  14. Hop someone finds it useful,
  15.  
  16. Edgar Butt (Butt@umd2.arpa) 
  17. Computer Science Center 
  18. University of Maryland  
  19. College Park, Maryland 20742
  20. (301) 454-2946  
  21.  
  22. The source for Kermit1100 version 2.0 begins on the next line.  
  23. {Kermit1100 - see first executable line in main block for version   
  24.  
  25.    KERMIT1100 is yet another Kermit written to run on the Sperry (Univac)   
  26.    1100 series of computers.  It is written in Pascal to be compiled on 
  27.    the NOSC Pascal Compiler, version 2.2 or later.  This compiler is
  28.    available from the Computer Science Center of the University of  
  29.    Maryland, College Park, for a nominal service charge.
  30.  
  31.    Kermit aficianodos may notice that the structure of this version 
  32.    differs from other versions in that packets are read and sequence
  33.    checked in the main program loop and are then dispatched to the  
  34.    proper input or output state with a single case statement.   
  35.    This structure has allowed the various state processes to be 
  36.    relatively uncluttered.  While doing this implementation I   
  37.    discovered that NAK's are like tadpole tails.  They seem like
  38.    a neat idea at first, but as the frog emerges, they serve no 
  39.    useful purpose.  Likewise, I have been unable to find a case 
  40.    in which NAK's are necessary.  Sending an ACK for the last   
  41.    good packet received is just as good.  If I'm wrong, I am sure   
  42.    that some swamp dweller out there will let me know.  
  43.    (Not to worry, I handle incoming NAK's even though they are not  
  44.    necessary.)  
  45.  
  46.    By way of a quick synopsys of features, this version of Kermit has:  
  47.  
  48.       Simple server mode - processes S and R packets
  49.       8-bit quoting (Turned on by Q-option) 
  50.       Repeat count prefixes 
  51.       Error packet generation and processing
  52.  
  53.    Kermit 1100 is called as a processor with the following control card:
  54.  
  55.       @Q*F.KERMIT,OPTIONS 1100SPEC,REMOTESPEC   
  56.  
  57.       Q*F. is the file in which the processor resides.  
  58.       1100SPEC is the 1100 file or element on which Kermit will operate.
  59.       REMOTESPEC is the file name sent to the remote Kermit(a fib of sorts) 
  60.       OPTIONS:  
  61.          B - big buffers.  Kermit1100 normally tells the remote Kermit to send
  62.              packets that will fit in 84 characters.  B-option causes it to
  63.              request the maximum size Kermit packets (which ain't as big as you
  64.              might wish)  Make sure that your communications hardware and
  65.              software will let the long packets get through.
  66.          C - assume for sending or receiving that records are to be separated
  67.              by CR instead of CR-LF 
  68.          L - log in the element KERMITLOG.MDSSS all file reads and writes and
  69.              all communication sends and receives. MDSSS is the month, day and
  70.              seconds/4 encoded base 32 (0,...,9,A,...,V). If a catalogued file
  71.              'KERMITLOG' is assignable, it is used.  Otherwise a temporary file
  72.              is created.
  73.          Q - allow eight-bit quoting for sending or receiving.  If the file 
  74.              being sent or received has 8-bit data and if the remote kermit 
  75.              is capable of 8-bit quoting, then all 8-bits of data can be
  76.              sent or received.  
  77.          R - expect to receive data.  Put the data in 1100SPEC if specified 
  78.              or in the file or element name sent from the remote Kermit.  No
  79.              transformation on the incoming name is done at present so it   
  80.              had better be good.
  81.          S - send 1100SPEC to the remote Kermit.  If REMOTESPEC is specified,
  82.              put it in the file header packet.  Otherwise put 1100SPEC in the
  83.              packet.
  84.          T - test mode.  Send (actually print on a terminal) packets as if  
  85.              an S-option had been specified without reading ACK's.
  86.          W - If the S-option is used, wait 30 seconds before starting to send
  87.  
  88. Kermit1100 tries not to exit until an EOF is received in order to process   
  89. multiple requests from the remote Kermit.   
  90.  
  91.    Happy hopping,   
  92.  
  93.       Edgar Butt  (BUTT@UMD2.ARPA)  
  94.       Computer Science Center   
  95.       University of Maryland
  96.       College Park, Maryland 20742  
  97.       Phone (301) 454-2946  
  98.  
  99. }   
  100. {$F Here we go.....}
  101.  
  102. PROCESSOR Kermit (input, output);   
  103.  
  104. CONST   
  105. maxtry = 5; 
  106. maxbuf = 200 ;  
  107. maxlin = 80;
  108. maxwrt = 132;   
  109.  
  110. ascnul = 0; 
  111. ascsoh = 1; 
  112. asclf  = 10;
  113. asccr  = 13;
  114. ascsp  = 32; { }
  115. ascns  = 35; {#}
  116. ascamp = 38; {&}
  117. ascast = 42; {*}
  118. ascper = 46; {.}
  119. ascb   = 66; {B}
  120. ascd   = 68; {D}
  121. asce   = 69; {E}
  122. ascf   = 70; {F}
  123. ascn   = 78; {N}
  124. ascr   = 82; {R}
  125. ascs   = 83; {S}
  126. asct   = 84; {T}
  127. ascy   = 89; {Y}
  128. ascz   = 90; {Z}
  129. asctil = 126; {~}   
  130. ascdel = 127; {rubout}  
  131.  
  132. mark = ascsoh;  
  133.  
  134. TYPE
  135. kermitstates = (kcommand,   
  136.                 kexit,  
  137.                 wexit,  
  138.                 sinitiate,  
  139.                 sheader,
  140.                 sdata,  
  141.                 sbreak, 
  142.                 rinitiate,  
  143.                 rheader,
  144.                 rdata); 
  145. filestatus = (closed, open, endfile);   
  146. ascval = 0..255 ;   
  147. ascbuf = RECORD 
  148.                ln: INTEGER; 
  149.                ch: ARRAY[1..maxbuf] OF ascval;  
  150.             END;
  151. line = PACKED ARRAY [1..maxlin] OF CHAR;
  152.  
  153. {System dependent TYPE} 
  154.  
  155. ident= PACKED ARRAY[1..12] OF CHAR; 
  156. sbits = SET of 0..35;   
  157.  
  158. VAR 
  159.  
  160. version: string;
  161. iniflg: boolean; {Set true after first initialization}  
  162. server: boolean; {If true, Kermit1100 waits for packets from remote}
  163. state: kermitstates;
  164. filbuf,wrtbuf,redbuf,sndbuf,rcvbuf: ascbuf; 
  165. redix:  integer;
  166. rfile,wfile,lfile: text;
  167. fname,rfname,lname: line;   
  168. fnlen,rfnlen: INTEGER;  
  169. rstatus, wstatus,lstatus: filestatus;   
  170. seq,rcvseq: INTEGER;
  171. rlen: INTEGER;  
  172. stype,rcvtyp: ascval;   
  173. numtry: INTEGER;
  174. numcserr: INTEGER;  
  175. ineoln: boolean;
  176. sndonly: boolean;   
  177. sndlog, rcvlog, wrtlog, redlog: boolean;
  178. bstrip:  boolean;   
  179. creol:  boolean;
  180. lfeol:  boolean;
  181. crlfeol:  boolean;  
  182. gotcr:  boolean;
  183.  
  184. locbsiz:  ascval;   
  185. loctout:  ascval;   
  186. locnpad:  ascval;   
  187. locpad:   ascval;   
  188. loceol:   ascval;   
  189. locquo:   ascval;   
  190. optqu8:   ascval;   
  191. locqu8:   ascval;   
  192. locrep:   ascval;   
  193.  
  194. rembsiz:  ascval;   
  195. remdsiz:  ascval; {Maximum number of data characters to send (remdsiz-3)}   
  196. remtout:  ascval;   
  197. remnpad:  ascval;   
  198. rempad:   ascval;   
  199. remeol:   ascval;   
  200. remquo:   ascval;   
  201. remqu8:   ascval;   
  202. remrep:   ascval;   
  203.  
  204. {System dependent VAR}  
  205. ruse,wuse,luse: ident;  
  206. a0,a1,a2: integer;  
  207.  
  208. {Forward reference procedures } 
  209.  
  210. PROCEDURE error(msg:string);FORWARD;
  211.  
  212. {System dependent procedures to read and write files}   
  213.  
  214. PROCEDURE readelt1(VAR f:text; filename:ident; name:line; VAR ok:boolean);  
  215.    EXTERN;  
  216. PROCEDURE openelt1(VAR f:text; filename:ident; name:line; VAR ok:boolean);  
  217.    EXTERN;  
  218. PROCEDURE closeelt1(VAR f:text; filename:ident; name:line); EXTERN; 
  219. PROCEDURE param_string(field:INTEGER; VAR param:STRING); EXTERN;
  220. PROCEDURE csf(image:line; VAR status:sbits);EXTERN; 
  221. PROCEDURE write_now(VAR f:text);EXTERN; 
  222.  
  223. {   
  224. System dependent procedure to get a file name from the procedure call card. 
  225. }   
  226. PROCEDURE getspec(field: INTEGER; VAR l: line; VAR len: INTEGER);   
  227. VAR s: string[80];  
  228.     i: INTEGER; 
  229.  
  230. BEGIN   
  231. param_string(field,s);  
  232. len:=LENGTH(s); 
  233. FOR i:=1 TO len DO l[i]:=s[i];  
  234. FOR i:=len+1 TO 80 DO l[i]:=' ';
  235. END;
  236.  
  237. {$F Character manipulation routines}
  238.  
  239. {System dependent:  It is assumed that the function ord(c) where
  240.  c is of type char will return the ASCII code for the character c.} 
  241.  
  242. {System dependent:  It is assumed that the function chr(i) where
  243.  i is an integer ASCII code from 0 to 255 will return the appropriate   
  244.  character} 
  245.  
  246. FUNCTION makechar (i: INTEGER): ascval; 
  247.  
  248. BEGIN   
  249. makechar:=ascsp+i;  
  250. END;
  251.  
  252. FUNCTION unchar (a: ascval): INTEGER;   
  253.  
  254. BEGIN   
  255. unchar:=a-ascsp;
  256. END;
  257.  
  258. FUNCTION tog64(a: ascval): ascval;  
  259.  
  260. BEGIN   
  261. tog64:=bxor(64,a); {System dependent}   
  262. END;
  263.  
  264. FUNCTION tog128(a: ascval): ascval; 
  265.  
  266. BEGIN   
  267. tog128:=bxor(128,a); {System dependent} 
  268. END;
  269.  
  270. FUNCTION checksum (sum: INTEGER): ascval;   
  271.  
  272. BEGIN   
  273. checksum := (((sum MOD 256) DIV 64) +  sum)  MOD 64;
  274. END;
  275. {$F Open and close log file}
  276. PROCEDURE logopn; {System dependent}
  277. VAR i,t: INTEGER;   
  278.     lstat: boolean; 
  279.     csfsta: sbits;  
  280.  
  281. BEGIN   
  282. csf('@asg,az kermitlog. ',csfsta);  
  283. IF 35 IN csfsta THEN
  284.    BEGIN
  285.    csf('@asg,t kermitlog.,///256 . ',csfsta);   
  286.    END; 
  287. IF 35 IN csfsta THEN
  288.    BEGIN
  289.    writeln(lfile,'Error assigning logfile: KERMITLOG'); 
  290.    END  
  291. ELSE
  292.    BEGIN
  293.    lname:='KERMITLOG.mdttt . '; 
  294.    er(44{TDATE$},a0);   
  295.    a1:=bshr(band(170000000000b,a0),10)+bshr(band(3700000000b,a0),9) 
  296.       +band(77777b,bshr(a0,2)); 
  297.    FOR i:=1 TO 5 DO 
  298.       BEGIN 
  299.       t:=band(31,bshlc(a1,11+5*i))+48;  
  300.       IF t>57 THEN t:=t+7;  
  301.       lname[10+i]:=chr(t);  
  302.       END;  
  303.    luse:='L$F$I$L$E$$$';
  304.    openelt1(lfile,luse,lname,lstat);
  305.    IF lstat=false THEN  
  306.       BEGIN 
  307.       writeln('Error opening log element:  ',lname);
  308.       END   
  309.    ELSE 
  310.       BEGIN 
  311.       lstatus:=open;
  312.       write(lfile,'Kermit1100 ',version,' Logfile ');   
  313.       write_now(lfile); {Write date and time into logfile}  
  314.       writeln(lfile);   
  315.       writeln(output,'Logging to ',lname);  
  316.       END;  
  317.    END; 
  318. END;
  319.  
  320. PROCEDURE logcls; {System dependent}
  321.  
  322. BEGIN   
  323. IF lstatus=open THEN
  324.    BEGIN
  325.    closeelt1(lfile,luse,lname); 
  326.    END; 
  327. END;
  328. {$F Buffer routines}
  329.  
  330. PROCEDURE bufinit(VAR buf:ascbuf);  
  331.  
  332. BEGIN   
  333. buf.ln:=0;  
  334. END;
  335.  
  336. PROCEDURE putbuf(VAR buf: ascbuf; a:ascval);
  337.  
  338. BEGIN   
  339. IF NOT (buf.ln<maxbuf) THEN 
  340.    BEGIN
  341.    error('Size of ascii buffer exceeded');  
  342.    END  
  343. ELSE
  344.    BEGIN
  345.    buf.ln:=buf.ln+1;
  346.    buf.ch[buf.ln]:=a;   
  347.    END; 
  348. END;
  349.  
  350. PROCEDURE lintobuf(l: line; len: integer; VAR buf: ascbuf); 
  351. VAR i:integer;  
  352.  
  353. BEGIN   
  354. bufinit(buf);   
  355. FOR i:=1 TO len DO putbuf(buf,ord(l[i]));   
  356. END;
  357.  
  358. PROCEDURE buftolin(buf: ascbuf; VAR l: line; VAR len: integer); 
  359. VAR i:integer;  
  360.     a:ascval;   
  361.  
  362. BEGIN   
  363. len:=buf.ln;
  364. IF len>maxlin THEN len:=maxlin; 
  365. FOR i:=1 TO len DO  
  366.    BEGIN
  367.    a:=buf.ch[i];
  368.    IF a>127 THEN a:=a-127;  
  369.    l[i]:=chr(a);
  370.    END; 
  371. FOR i:=len+1 to maxlin DO l[i]:=' ';
  372. END;
  373. {$F Process parameters to and from remote Kermit}   
  374. PROCEDURE putpar;   
  375. VAR temp: ascval;   
  376.  
  377. BEGIN   
  378. bufinit(filbuf);
  379. putbuf(filbuf,makechar(locbsiz));   
  380. putbuf(filbuf,makechar(loctout));   
  381. putbuf(filbuf,makechar(locnpad));   
  382. putbuf(filbuf,tog64(locpad));   
  383. putbuf(filbuf,makechar(loceol));
  384. putbuf(filbuf,locquo);  
  385. temp:=ascsp;
  386. IF locqu8<>0 THEN temp:=locqu8; 
  387. putbuf(filbuf,temp);
  388. putbuf(filbuf,ascsp); {Only know how do to 1 character checksum}
  389. temp:=ascsp;
  390. IF locrep<>0 THEN temp:=locrep; 
  391. putbuf(filbuf,temp);
  392. END;
  393.  
  394. PROCEDURE getpar;   
  395.  
  396. BEGIN   
  397. IF rcvbuf.ln > 0 THEN rembsiz:=unchar(rcvbuf.ch[1]);
  398. IF rcvbuf.ln > 1 THEN remtout:=unchar(rcvbuf.ch[2]);
  399. IF rcvbuf.ln > 2 THEN remnpad:=unchar(rcvbuf.ch[3]);
  400. IF rcvbuf.ln > 3 THEN rempad:=tog64(rcvbuf.ch[4]);  
  401. IF rcvbuf.ln > 4 THEN remeol:=unchar(rcvbuf.ch[5]); 
  402. IF rcvbuf.ln > 5 THEN remquo:=rcvbuf.ch[6]; 
  403. IF rcvbuf.ln > 6 THEN remqu8:=rcvbuf.ch[7]; 
  404. IF rcvbuf.ln > 8 THEN remrep:=rcvbuf.ch[9]; 
  405.  
  406. remdsiz:=rembsiz-3; 
  407. IF state=rinitiate THEN {Our parameters have not been sent} 
  408.    BEGIN
  409.    IF locqu8=0 THEN remqu8:=0;  
  410.    IF ((32<remqu8) AND (remqu8<63)) OR ((95<remqu8) AND (remqu8<127))   
  411.    AND (remqu8<>remquo) THEN
  412.       BEGIN 
  413.       locqu8:=ascy; {Remote Kermit specified 8-bit quote character} 
  414.       END   
  415.    ELSE IF remqu8=ascy THEN 
  416.       BEGIN 
  417.       locqu8:=ascamp;   
  418.       IF (locqu8=remquo) OR (locqu8=remrep) THEN locqu8:=asctil;
  419.       IF (locqu8=remquo) OR (locqu8=remrep) THEN locqu8:=ascns; 
  420.       remqu8:=locqu8;   
  421.       END   
  422.    ELSE 
  423.       BEGIN 
  424.       locqu8:=0;        {Don't do 8-bit quoting}
  425.       remqu8:=0;
  426.       END;  
  427.    IF ((32<remrep) AND (remrep<63)) OR ((95<remrep) AND (remrep<127))   
  428.    AND (remrep<>remquo) AND (remrep<>remqu8) AND (locrep<>0) THEN   
  429.       BEGIN 
  430.       locrep:=remrep; {Agree to do repeat counts}   
  431.       END   
  432.    ELSE 
  433.       BEGIN 
  434.       remrep:=0;
  435.       locrep:=0;
  436.       END;  
  437.    END  
  438. ELSE {Our parameters have already been sent}
  439.    BEGIN
  440.    IF (remqu8<>ascy) AND (remqu8<>locqu8) THEN  
  441.       BEGIN 
  442.       locqu8:=0; {Don't do 8-bit quoting}   
  443.       END;  
  444.    IF remrep<>locrep THEN locrep:=0; {Don't do repeat counts}   
  445.    END; 
  446. END;
  447. {$F Input a packet or a command}
  448. PROCEDURE rcvpkt;   
  449. {   
  450. This procedure reads all terminal input to Kermit, both packets and 
  451. command lines.  On exit, the following global parameters are set:   
  452.  
  453.    rcvtyp = 0 - No SOH encountered, could be command line   
  454.             1 - SOH encountered, but packet incomplete  
  455.             2 - Checksum error  
  456.             Other - ASCII value of packet type from good packet 
  457.  
  458.    rcvseq = -1 - Not a valid packet 
  459.             -2 - End of input file encountered  
  460.             0...63 - Sequence number from valid packet  
  461.  
  462.    rcvbuf.ln - number of ascii values input since last SOH or   
  463.                if no SOH, from beginning of line
  464.    rcvbuf.ch - array of ascii values input  
  465. }   
  466. VAR c: CHAR;
  467.     av,rt: ascval;  
  468.     rst,rsq,cs:INTEGER; 
  469.  
  470. BEGIN   
  471. IF rcvlog THEN write(lfile,'rcv <');
  472. IF ineoln THEN  
  473.    BEGIN
  474.    readln(input);   
  475.    END; 
  476. rcvtyp:=0;  
  477. IF eof(input) THEN  
  478.    BEGIN
  479.    rcvseq:=-2;  
  480.    IF rcvlog THEN write(lfile,'@'); 
  481.    END  
  482. ELSE
  483.    BEGIN
  484.    rcvseq:=-1;  
  485.    rst:=0;  
  486.    ineoln:=eoln(input); 
  487.    bufinit(rcvbuf); 
  488.    WHILE NOT ineoln DO  
  489.       BEGIN 
  490.       IF eoln(input) THEN   
  491.          BEGIN  
  492.          {  
  493.          The 1100 EXEC truncates some trailing spaces.  Since a 
  494.          valid packet can end in one or more spaces, we will assume 
  495.          that short packets should end in spaces and hope that  
  496.          the checksum filters out errors.   
  497.          }  
  498.          av:=ascsp; 
  499.          END
  500.       ELSE  
  501.          BEGIN  
  502.          read(input,c); 
  503.          IF rcvlog THEN write(lfile,c); 
  504.          av:=ord(c);
  505.          END;   
  506.       IF av=mark THEN rst:=1;   
  507.       CASE rst OF   
  508.  
  509.          0: {Mark character never encountered.} 
  510.             BEGIN   
  511.             putbuf(rcvbuf,av);  
  512.             ineoln:=eoln(input);
  513.             END;
  514.  
  515.          1: {Mark character.}   
  516.             BEGIN   
  517.             rcvtyp:=1;  
  518.             rcvseq:=-1; 
  519.             bufinit(rcvbuf);
  520.             ineoln:=eoln(input);
  521.             rst:=2; 
  522.             END;
  523.  
  524.          2: {Length of the packet.} 
  525.             BEGIN   
  526.             cs:=av; {Initialize checksum}   
  527.             rlen:=unchar(av)-3; 
  528.             rst:=3; 
  529.             END;
  530.  
  531.          3: {Packet number.}
  532.             BEGIN   
  533.             cs:=cs+av;  
  534.             rsq:=unchar(av);
  535.             rst:=4; 
  536.             END;
  537.  
  538.          4: {Packet type.}  
  539.             BEGIN   
  540.             cs:=cs+av;  
  541.             rt:=av; {remember the packet type}  
  542.             rst:=5; 
  543.             IF rlen=0 THEN rst:=6;  
  544.             END;
  545.  
  546.          5: {Data portion.} 
  547.             BEGIN   
  548.             cs:=cs+av;  
  549.             putbuf(rcvbuf,av);  
  550.             IF rcvbuf.ln = rlen THEN rst:=6;
  551.             END;
  552.  
  553.          6: {Checksum.} 
  554.             BEGIN   
  555.             IF checksum(cs)=unchar(av) THEN 
  556.                BEGIN
  557.                rcvtyp:=rt;  
  558.                rcvseq:=rsq; 
  559.                ineoln:=true; {Ignore the rest of the line}  
  560.                END  
  561.             ELSE
  562.                BEGIN
  563.                numcserr:=numcserr+1;
  564.                rst:=0; {Look for another mark}  
  565.                rcvtyp:=2;  {Indicate checksum error}
  566.                ineoln:=eoln(input); 
  567.                END; 
  568.             END;
  569.          END;   
  570.       END;  
  571.    END; 
  572. IF rcvlog THEN writeln(lfile,'>');  
  573. END;
  574. {$F Build and send packets} 
  575. PROCEDURE makepacket(ptype: ascval; seq, len: INTEGER); 
  576. VAR i: INTEGER; 
  577.     c: ascval;  
  578.     cs: INTEGER;
  579.  
  580. BEGIN   
  581. bufinit(sndbuf);
  582. FOR i:=1 TO remnpad DO  
  583.    BEGIN
  584.    putbuf(sndbuf,rempad);   
  585.    END; 
  586. putbuf(sndbuf,mark);
  587. c:=makechar(len+3); 
  588. cs:=c;                              {Initialize checksum}   
  589. putbuf(sndbuf,c);   
  590. c:=makechar(seq);   
  591. cs:=cs+c;   
  592. putbuf(sndbuf,c);   
  593. c:=ptype;   
  594. cs:=cs+c;   
  595. putbuf(sndbuf,c);   
  596. FOR i:=1 to len DO  
  597.    BEGIN
  598.    c:=filbuf.ch[i]; 
  599.    cs:=cs+c;
  600.    putbuf(sndbuf,c);
  601. END;
  602. c:=makechar(checksum(cs));  
  603. putbuf(sndbuf,c);   
  604. {   
  605. The 1100 EXEC may strip trailing spaces from the end of output images.  
  606. This can cause a problem if the checksum is a space.  To eliminate this 
  607. problem, a period will be inserted in the output image after the
  608. checksum whenever the checksum is a space.  
  609. }   
  610. putbuf(sndbuf,ascper);  
  611. {   
  612. The 1100 O/S puts a CR LF on the end of each output line.   
  613. If the remote EOL character is not CR or LF, then it must   
  614. be added to the packet. 
  615. }   
  616. IF (remeol<>asccr) AND (remeol<>asclf) THEN 
  617.    BEGIN
  618.    putbuf(sndbuf,remeol);   
  619.    END; 
  620. END;
  621.  
  622. PROCEDURE sndpkt;   
  623. VAR 
  624.    i:INTEGER;   
  625.  
  626. BEGIN   
  627. IF sndlog THEN write(lfile,'snd <');
  628. FOR i:=1 TO sndbuf.ln DO
  629.    BEGIN
  630.    write(output,chr(sndbuf.ch[i])); 
  631.    IF sndlog THEN write(lfile,chr(sndbuf.ch[i]));   
  632. END;
  633. writeln(output);
  634. IF sndlog THEN writeln(lfile,'>');  
  635. END;
  636. {$F File output}
  637.  
  638. PROCEDURE wrtrec;   
  639. VAR 
  640.    i:INTEGER;   
  641.    c:char;  
  642.  
  643. BEGIN   
  644. IF wrtlog THEN write(lfile,'wrt [');
  645. FOR i:=1 TO wrtbuf.ln DO
  646.    BEGIN
  647.    {$A- Turn off range checking, ASCII value may be >127}   
  648.    c:=chr(wrtbuf.ch[i]);
  649.    {$A+ Turn on range checking} 
  650.    write(wfile,c) ; 
  651.    IF wrtlog THEN write(lfile,c);   
  652.    END; 
  653. writeln(wfile); 
  654. IF wrtlog THEN writeln(lfile,']');  
  655. bufinit(wrtbuf);
  656. END;
  657.  
  658. PROCEDURE wrtcls; {System dependent}
  659.  
  660. BEGIN   
  661. IF wstatus=open THEN
  662.    BEGIN
  663.    IF wrtbuf.ln>0 THEN wrtrec;  
  664.    closeelt1(wfile,wuse,fname); 
  665.    END; 
  666. wstatus:=closed;
  667. END;
  668.  
  669. PROCEDURE wrtopn; {System dependent}
  670. VAR 
  671. wstat: boolean; 
  672.  
  673. BEGIN   
  674. wrtcls; 
  675. wuse:='W$F$I$L$E$$$';   
  676. openelt1(wfile,wuse,fname,wstat);   
  677. IF wstat THEN wstatus:=open;
  678. bufinit(wrtbuf);
  679. END;
  680.  
  681. PROCEDURE wrtasc(a:ascval); 
  682.  
  683. BEGIN   
  684. IF wrtbuf.ln >=maxwrt THEN wrtrec;  
  685. putbuf(wrtbuf,a);   
  686. END;
  687. {$F Process data portion of data packet}
  688. PROCEDURE putrec(buf: ascbuf);  
  689. VAR 
  690.    i,j,repcnt:INTEGER;  
  691.    a:ascval;
  692.    qflag: boolean;  
  693.  
  694. BEGIN   
  695. i:=1;   
  696. WHILE i<= buf.ln DO 
  697.    BEGIN
  698.    a:=buf.ch[i]; i:=i+1;
  699.    repcnt:=1;   
  700.    IF a=remrep THEN 
  701.       BEGIN 
  702.       repcnt:=unchar(buf.ch[i]); i:=i+1;
  703.       a:=buf.ch[i]; i:=i+1; 
  704.       END;  
  705.    qflag:= a=remqu8;
  706.    IF qflag THEN
  707.       BEGIN 
  708.       a:=buf.ch[i]; i:=i+1; 
  709.       END;  
  710.    IF a=remquo THEN 
  711.       BEGIN 
  712.       a:=buf.ch[i]; i:=i+1; 
  713.       IF (a<>remquo) AND (a<>remqu8) AND (a<>remrep) THEN a:=tog64(a);  
  714.       END;  
  715.    IF qflag THEN a:=tog128(a);  
  716.    FOR j:=1 to repcnt DO
  717.       BEGIN 
  718.       IF a=asclf THEN   
  719.          BEGIN  
  720.          IF lfeol OR gotcr THEN 
  721.             BEGIN   
  722.             wrtrec; 
  723.             gotcr:=false;   
  724.             END 
  725.          ELSE   
  726.             BEGIN   
  727.             wrtasc(a);  
  728.             END;
  729.          END
  730.       ELSE  
  731.          BEGIN  
  732.          IF gotcr THEN  
  733.             BEGIN   
  734.             wrtasc(asccr);  
  735.             gotcr:=false;   
  736.             END;
  737.          IF a=asccr THEN
  738.             BEGIN   
  739.             IF creol THEN   
  740.                BEGIN
  741.                wrtrec;  
  742.                END  
  743.             ELSE IF crlfeol THEN
  744.                BEGIN
  745.                gotcr:=true; 
  746.                END  
  747.             ELSE
  748.                BEGIN
  749.                wrtasc(a);   
  750.                END; 
  751.             END 
  752.          ELSE   
  753.             BEGIN   
  754.             wrtasc(a);  
  755.             END;
  756.          END;   
  757.       END;  
  758.    END; 
  759. END;
  760. {$F File input} 
  761. PROCEDURE redrec;   
  762. VAR c: CHAR;
  763.     a: ascval;  
  764.     nonblank: INTEGER;  
  765.  
  766. BEGIN   
  767. bufinit(redbuf);
  768. IF redix >= 0 THEN readln(rfile);   
  769. redix:=0;   
  770. IF NOT eof(rfile) THEN  
  771.    BEGIN
  772.    nonblank:=0; 
  773.    IF redlog THEN write(lfile,'red ['); 
  774.    WHILE NOT eoln(rfile) DO 
  775.       BEGIN 
  776.       read(rfile,c);
  777.       IF redlog THEN write(lfile,c);
  778.       a:=ord(c);
  779.       putbuf(redbuf,a); 
  780.       IF a <> ascsp THEN nonblank := redbuf.ln; 
  781.       END;  
  782.    IF redlog THEN writeln(lfile,']');   
  783.    IF bstrip THEN redbuf.ln := nonblank;
  784.    IF creol OR crlfeol THEN putbuf(redbuf,asccr);   
  785.    IF lfeol OR crlfeol THEN putbuf(redbuf,asclf);   
  786.    END; 
  787. END;
  788.  
  789. PROCEDURE redopn; {System dependent}
  790. VAR 
  791. rstat: boolean; 
  792.  
  793. BEGIN   
  794. rstatus:=closed;
  795. ruse:='R$F$I$L$E$$$';   
  796. readelt1(rfile,ruse,fname,rstat);   
  797. IF rstat THEN rstatus:=open;
  798. redix:=-1;  
  799. redbuf.ln:=-1;  
  800. END;
  801.  
  802. PROCEDURE redcls;   
  803.  
  804. BEGIN   
  805. rstatus:=closed;
  806. END;
  807.  
  808. {$F Build data portion of data packet}  
  809. PROCEDURE getrec;   
  810. VAR a: ascval;  
  811.     exit: BOOLEAN;  
  812.     prevln,previx,tix: INTEGER; 
  813.  
  814. BEGIN   
  815. bufinit(filbuf);
  816. IF eof(rfile) THEN  
  817.    BEGIN
  818.    rstatus:=endfile;
  819.    END  
  820. ELSE
  821.    BEGIN
  822.    exit:=false; 
  823.    REPEAT   
  824.       IF redix >= redbuf.ln THEN
  825.          BEGIN  
  826.          redrec; {get another record and strip spaces}  
  827.          IF eof(rfile) THEN 
  828.             BEGIN   
  829.             exit:=true; 
  830.             IF filbuf.ln=0 THEN rstatus:=endfile;   
  831.             END;
  832.          END;   
  833.       IF redix < redbuf.ln THEN 
  834.          BEGIN  
  835.          prevln:=filbuf.ln; 
  836.          previx:=redix; 
  837.          redix:=redix+1;
  838.          a:=redbuf.ch[redix];   
  839.          IF locrep<>0 THEN  
  840.             BEGIN   
  841.             tix:=redix+1;   
  842.             WHILE (a=redbuf.ch[tix]) AND (tix<=redbuf.ln) DO tix:=tix+1;
  843.             tix:=tix-redix;   {tix is now the repeat count} 
  844.             IF tix>3 THEN   
  845.                BEGIN
  846.                IF tix>94 THEN tix:=94;  
  847.                putbuf(filbuf,locrep);   
  848.                putbuf(filbuf,makechar(tix));
  849.                redix:=redix-1+tix;  
  850.                END; 
  851.             END;
  852.          IF (a>127) THEN
  853.             BEGIN   
  854.             IF locqu8<>0 THEN putbuf(filbuf,locqu8);
  855.             a:=tog128(a);   
  856.             END;
  857.          IF (a<32) OR (a=ascdel) THEN   
  858.             BEGIN   
  859.             putbuf(filbuf,locquo);  
  860.             a:=tog64(a);
  861.             END;
  862.          IF (a=locquo) OR (a=locqu8) OR (a=locrep) THEN 
  863.             BEGIN   
  864.             putbuf(filbuf,locquo);  
  865.             END;
  866.          putbuf(filbuf,a);  
  867.          IF filbuf.ln >= remdsiz THEN   
  868.             BEGIN   
  869.             exit:=true; 
  870.             IF filbuf.ln>remdsiz then   
  871.                BEGIN
  872.                {Character expansion caused buffer length to be  
  873.                 exceeded.  Back up.}
  874.                filbuf.ln:=prevln;   
  875.                redix:=previx;   
  876.                END; 
  877.             END;
  878.          END;   
  879.    UNTIL exit;  
  880.    END; 
  881. END;
  882.  
  883. {$F Send states}
  884. PROCEDURE sendinitiate; 
  885.  
  886. BEGIN   
  887. IF fnlen>0 THEN 
  888.    BEGIN
  889.    redopn;  
  890.    IF rstatus=open THEN 
  891.       BEGIN 
  892.       putpar; {Put parameters into buffer}  
  893.       makepacket(ascs,seq,filbuf.ln); {Make packet with our parameters} 
  894.       numtry:=0;
  895.       state:=sheader;   
  896.       END   
  897.    ELSE 
  898.       BEGIN 
  899.       error('Error opening read file'); 
  900.       state:=kexit; 
  901.       END;  
  902.    END  
  903. ELSE
  904.    BEGIN
  905.    error('No read file specified'); 
  906.    state:=kexit;
  907.    END; 
  908. END;
  909.  
  910. PROCEDURE sendheader;   
  911.  
  912. BEGIN   
  913. IF rcvtyp=ascy THEN 
  914.    BEGIN
  915.    IF not sndonly THEN getpar; {Get parameters from ACK of 'S' packet}  
  916.    IF rfnlen>0 THEN 
  917.       BEGIN 
  918.       lintobuf(rfname,rfnlen,filbuf); {Send remote file name.}  
  919.       END   
  920.    ELSE 
  921.       BEGIN 
  922.       lintobuf(fname,fnlen,filbuf);   {Send local file name.}   
  923.       END;  
  924.    numtry:=0;   
  925.    seq:=(seq+1) mod 64; 
  926.    makepacket(ascf,seq,filbuf.ln);  
  927.    state:=sdata 
  928.    END; 
  929. END;
  930.  
  931. PROCEDURE senddata; 
  932.  
  933. BEGIN   
  934. IF rcvtyp=ascy THEN 
  935.    BEGIN
  936.    getrec;  
  937.    numtry:=0;   
  938.    seq:=(seq+1) mod 64; 
  939.    IF rstatus = open THEN   
  940.       BEGIN 
  941.       makepacket(ascd,seq,filbuf.ln);   
  942.       END   
  943.    ELSE 
  944.       BEGIN 
  945.       makepacket(ascz,seq,0);   
  946.       state:=sbreak;
  947.       fnlen:=0; 
  948.       END;  
  949.    END; 
  950. END;
  951.  
  952. PROCEDURE sendbreak;
  953.  
  954. BEGIN   
  955. IF rcvtyp=ascy THEN 
  956.    BEGIN
  957.    numtry:=0;   
  958.    seq:=(seq+1) mod 64; 
  959.    makepacket(ascb,seq,0);  
  960.    END; 
  961. state:=wexit;   
  962. END;
  963. {$F Receive states} 
  964. PROCEDURE receiveinitiate;  
  965.  
  966. BEGIN   
  967. IF rcvtyp=ascs  THEN
  968.    BEGIN
  969.    getpar; {Get parameters from packet} 
  970.    putpar; {Put parameters into buffer} 
  971.    makepacket(ascy,seq,filbuf.ln); {Make ACK packet with our parameters}
  972.    seq:=rcvseq; 
  973.    numtry:=0;   
  974.    seq:=(seq+1) mod 64; 
  975.    state:=rheader;  
  976.    END  
  977. ELSE
  978.    BEGIN
  979.    error('Wrong packet in receive initiation'); 
  980.    state:=kexit;
  981.    END; 
  982. END;
  983.  
  984. PROCEDURE receiveheader;
  985.  
  986. BEGIN   
  987. IF rcvtyp=ascf THEN 
  988.    BEGIN
  989.    IF fnlen=0 THEN  
  990.       BEGIN 
  991.       buftolin(rcvbuf,fname,fnlen); 
  992.       END;  
  993.    IF fnlen>0 THEN  
  994.       BEGIN 
  995.       wrtopn;   
  996.       IF wstatus=open THEN  
  997.          BEGIN  
  998.          makepacket(ascy,seq,0);
  999.          numtry:=0; 
  1000.          seq:=(seq+1) mod 64;   
  1001.          state:=rdata;  
  1002.          END
  1003.       ELSE  
  1004.          BEGIN  
  1005.          error('Error opening write file'); 
  1006.          state:=kexit;  
  1007.          END;   
  1008.       END   
  1009.    ELSE 
  1010.       BEGIN 
  1011.       error('No output file specified');
  1012.       state:=kexit; 
  1013.       END;  
  1014.    END  
  1015. ELSE IF rcvtyp=ascb THEN
  1016.    BEGIN
  1017.    makepacket(ascy,seq,0);  
  1018.    sndpkt;  
  1019.    state:=kexit;
  1020.    END  
  1021. ELSE
  1022.    BEGIN
  1023.    error('Wrong packet receiveing file header');
  1024.    state:=kexit;
  1025.    END; 
  1026. END;
  1027.  
  1028. PROCEDURE receivedata;  
  1029.  
  1030. BEGIN   
  1031. IF rcvtyp=ascd THEN 
  1032.    BEGIN
  1033.    putrec(rcvbuf);  
  1034.    makepacket(ascy,seq,0);  
  1035.    numtry:=0;   
  1036.    seq:=(seq+1) mod 64; 
  1037.    END  
  1038. ELSE IF rcvtyp=ascz THEN
  1039.    BEGIN
  1040.    wrtcls;  
  1041.    fnlen:=0;
  1042.    makepacket(ascy,seq,0);  
  1043.    numtry:=0;   
  1044.    seq:=(seq+1) mod 64; 
  1045.    state:=rheader;  
  1046.    END  
  1047. ELSE
  1048.    BEGIN
  1049.    error('Unexpected packet receiving data');   
  1050.    state:=kexit;
  1051.    END; 
  1052. END;
  1053. {$F Error processing}   
  1054.  
  1055. {Process fatal errors}  
  1056.  
  1057. PROCEDURE error; {parameters appear above in forward reference} 
  1058. VAR i,l:integer;
  1059.  
  1060. BEGIN   
  1061. l:=length(msg); 
  1062. IF l>maxbuf-6 THEN l:=maxbuf-6; 
  1063. bufinit(filbuf);
  1064. FOR i:=1 to 3 DO putbuf(filbuf,ascsp); {Make message readable in packet}
  1065. FOR i:=1 to l DO putbuf(filbuf,ord(msg[i]));
  1066. FOR i:=1 to 3 DO putbuf(filbuf,ascsp); {Make message readable in packet}
  1067. makepacket(asce,seq,filbuf.ln); 
  1068. sndpkt; 
  1069. state:=kexit;   
  1070. END;
  1071. {$F Command state}  
  1072. PROCEDURE kermitcommand;
  1073.  
  1074. BEGIN   
  1075. REPEAT  
  1076.    rcvpkt;  
  1077.    IF rcvseq>-1 THEN
  1078.       BEGIN 
  1079.       IF rcvtyp=ascs THEN   
  1080.          BEGIN  
  1081.          state:=rinitiate;  
  1082.          END
  1083.       ELSE IF rcvtyp=ascr THEN  
  1084.          BEGIN  
  1085.          IF fnlen=0 THEN
  1086.             BEGIN   
  1087.             buftolin(rcvbuf,fname,fnlen);   
  1088.             END;
  1089.          state:=sinitiate;  
  1090.          END
  1091.       ELSE  
  1092.          BEGIN  
  1093.          error('Unexpected packet type');   
  1094.          END;   
  1095.       END   
  1096.    ELSE IF rcvseq=-1 THEN   
  1097.       BEGIN 
  1098.       writeln('No commands implemented');   
  1099.       END   
  1100.    ELSE IF rcvseq=-2 THEN   
  1101.       BEGIN 
  1102.       state:=kexit; 
  1103.       server:=false;
  1104.       END;  
  1105. UNTIL state<>kcommand;  
  1106. END;
  1107. {$F Get processor call options and file specifications} 
  1108.  
  1109. PROCEDURE getoptions; {System dependent}
  1110.  
  1111. BEGIN   
  1112. getspec(1,fname,fnlen);   {Get local file name, if any.}
  1113. getspec(2,rfname,rfnlen); {Get remote file name, if any.}   
  1114. IF 'S' IN options THEN state:=sinitiate;
  1115. IF 'R' IN options THEN state:=rinitiate;
  1116. IF 'T' IN options THEN  
  1117.    BEGIN
  1118.    sndonly:=true;   
  1119.    state:=sinitiate;
  1120.    server:=false;   
  1121.    END; 
  1122. IF 'B' IN options THEN  
  1123.    BEGIN
  1124.    locbsiz:=94; 
  1125.    END; 
  1126. IF 'C' IN options THEN  
  1127.    BEGIN
  1128.    crlfeol:=false;  
  1129.    creol:=true; 
  1130.    lfeol:=false;
  1131.    END; 
  1132. IF 'L' IN options THEN  
  1133.    BEGIN
  1134.    sndlog:=true;
  1135.    rcvlog:=true;
  1136.    wrtlog:=true;
  1137.    redlog:=true;
  1138.    END; 
  1139. optqu8:=0; {Assume no eight-bit quoting will be done}   
  1140. IF 'Q' IN options THEN  
  1141.    BEGIN
  1142.    optqu8:=ascamp; {Eight-bit quoting may be done}  
  1143.    END; 
  1144. IF ('W' IN options) AND ('S' IN options) THEN   
  1145.    BEGIN
  1146.    a1:=30000;   
  1147.    er(48{TWAIT$},a0,a1);
  1148.    END; 
  1149. END;
  1150. {$F Initialization state}   
  1151. PROCEDURE kermitinitialize; 
  1152. VAR lstat: boolean; 
  1153.  
  1154. BEGIN   
  1155. state:=kcommand;
  1156. numtry:=0;  
  1157. seq:=0; 
  1158. fnlen:=0; {Indicate no file name yet}   
  1159. bstrip:=true;   
  1160.  
  1161. locbsiz:=78;
  1162. loctout:=12;
  1163. locnpad:=0; 
  1164. locpad:=0;  
  1165. loceol:=asccr;  
  1166. locquo:=ascns;  
  1167. {  locqu8 will be set after options are processed. }
  1168. locrep:=asctil; {Initialize to 0 to turn off repeat counts} 
  1169.  
  1170. rembsiz:=78;
  1171. remdsiz:=rembsiz-3; 
  1172. remtout:=12;
  1173. remnpad:=0; 
  1174. rempad:=0;  
  1175. remeol:=asccr;  
  1176. remqu8:=0;  
  1177. remrep:=0;  
  1178.  
  1179. bufinit(sndbuf);
  1180.  
  1181. {The following should only be done on the first call to initialize} 
  1182. IF iniflg=false THEN
  1183.    BEGIN
  1184.    sndonly:=false;  
  1185.    sndlog:=false;   
  1186.    rcvlog:=false;   
  1187.    wrtlog:=false;   
  1188.    redlog:=false;   
  1189.    crlfeol:=true;   
  1190.    creol:=false;
  1191.    lfeol:=false;
  1192.    rstatus:=closed; 
  1193.    wstatus:=closed; 
  1194.    lstatus:=closed; 
  1195.  
  1196.    {System dependent initialization}
  1197.    ineoln:=false;  {Indicate no readln necessary for first line}
  1198.    getoptions;     {Process options and file specifications}
  1199.    IF sndlog OR rcvlog OR wrtlog OR redlog THEN logopn  
  1200.    END; 
  1201. locqu8:=optqu8; {Eight-bit quoting done only with Q-option} 
  1202. iniflg:=true;   
  1203. END;
  1204. {$F Main block} 
  1205.  
  1206.  
  1207. BEGIN   
  1208. version:= '2.0';
  1209. writeln(output,'Kermit 1100 ',version); 
  1210. iniflg:=false;  
  1211. server:=true;   
  1212. WHILE server DO 
  1213.    BEGIN
  1214.    kermitinitialize;
  1215.    IF state=kcommand THEN kermitcommand;
  1216.    IF state=sinitiate THEN sendinitiate;
  1217.    IF state=rinitiate THEN receiveinitiate; 
  1218.    WHILE state<>kexit DO
  1219.       BEGIN 
  1220.       REPEAT
  1221.          sndpkt;
  1222.          numtry:=numtry+1;  
  1223.          IF sndonly THEN
  1224.             BEGIN   
  1225.             rcvseq:=seq;
  1226.             rcvtyp:=ascy;   
  1227.             rcvbuf.ln:=0;   
  1228.             END 
  1229.          ELSE   
  1230.             BEGIN   
  1231.             rcvpkt; 
  1232.             END;
  1233.          IF rcvtyp=ascn THEN
  1234.             BEGIN   
  1235.             {We have just received a NAK.  The Kermit protocol would
  1236.             be much simpler and no less effective if the NAK had never  
  1237.             been included.  However, since this is not universally  
  1238.             appreciated, one has to deal with them.  To do so, we   
  1239.             will convert a NAK into an ACK with the previous sequence   
  1240.             number.}
  1241.             rcvseq:=(rcvseq-1) mod 64;  
  1242.             rcvtyp:=ascy;   
  1243.             END 
  1244.          ELSE IF rcvseq=-2 THEN {End of file on input}  
  1245.             BEGIN   
  1246.             error('End of file on input data'); 
  1247.             state:=kexit;   
  1248.             server:=false;  
  1249.             END;
  1250.       UNTIL (rcvseq=seq) OR (numtry>=maxtry) OR (state=kexit);  
  1251.       IF (rcvseq<>seq) AND (state<>kexit) THEN  
  1252.          BEGIN  
  1253.          error('Failed to receive expected packet');
  1254.          state:=kexit;  
  1255.          END
  1256.       ELSE IF rcvtyp=asce THEN {Just received error packet} 
  1257.          BEGIN  
  1258.          state:=kexit   
  1259.          END
  1260.       ELSE  
  1261.          BEGIN  
  1262.          CASE state OF  
  1263.             sheader     :sendheader;
  1264.             sdata       :senddata;  
  1265.             sbreak      :sendbreak; 
  1266.             rheader     :receiveheader; 
  1267.             rdata       :receivedata;   
  1268.             wexit       :state:=kexit; {Go around one more time, then exit} 
  1269.             kexit       :;  
  1270.             END;
  1271.          END
  1272.    END; 
  1273.    wrtcls;  
  1274.    END; 
  1275. logcls; 
  1276. writeln('Kermit End');  
  1277. END .   
  1278.