1 /* C K C F N 2 -- System-independent Kermit protocol support functions... */
3 /* ...Part 2 (continued from ckcfns.c) */
6 Author: Frank da Cruz <fdc@columbia.edu>,
7 Columbia University Academic Information Systems, New York City.
9 Copyright (C) 1985, 2011,
10 Trustees of Columbia University in the City of New York.
11 All rights reserved. See the C-Kermit COPYING.TXT file or the
12 copyright text in the ckcmai.c module for disclaimer and permissions.
15 Note -- if you change this file, please amend the version number and date at
16 the top of ckcfns.c accordingly.
19 #include "ckcsym.h" /* Compilation options */
20 #include "ckcdeb.h" /* Debugging and other symbols */
21 #include "ckcasc.h" /* ASCII symbols */
22 #include "ckcker.h" /* Kermit symbols */
23 #include "ckcxla.h" /* Translation */
24 #include "ckcnet.h" /* IKS and VMS #define TCPSOCKET */
25 #ifdef TCPSOCKET /* For TELNET business in spack() */
26 extern int tn_nlm, ttnproto, tn_b_nlm;
27 #endif /* TCPSOCKET */
29 extern int parity, network, local, interrupted, fatalio, wasclosed;
31 int kstartactive = 0; /* Flag for kstart() in a packet */
33 static CHAR p_tbl[] = { /* Even parity table for dopar(). */
34 (CHAR) '\000', /* ANSI C casts '\ooo' constants */
35 (CHAR) '\201', /* to signed char, so we have to */
36 (CHAR) '\202', /* cast back to unsigned char... */
164 /* D O P A R -- Add an appropriate parity bit to a character */
168 dopar(register CHAR ch)
170 dopar(ch) register CHAR ch;
171 #endif /* CK_ANSIC */
173 register unsigned int a;
176 || (network && (ttnproto == NP_TELNET) && (TELOPT_ME(TELOPT_BINARY)))
178 || (!local && sstelnet) /* TELNET BINARY MODE */
180 #endif /* TCPSOCKET */
181 ) return((CHAR) (ch & 255)); else a = ch & 127;
183 case 'e': return(p_tbl[a]); /* Even */
184 case 'm': return((CHAR) (a | 128)); /* Mark */
185 case 'o': return((CHAR) (p_tbl[a] ^ 128)); /* Odd */
186 case 's': return((CHAR) a); /* Space */
187 default: return((CHAR) a); /* Something illegal */
191 #ifndef NOXFER /* Rest of this file... */
193 #define NEWDPL /* New dynamic packet length method */
202 extern struct pktinfo *s_pkt; /* array of pktinfo structures */
203 extern struct pktinfo *r_pkt; /* array of pktinfo structures */
205 extern struct pktinfo s_pkt[]; /* array of pktinfo structures */
206 extern struct pktinfo r_pkt[]; /* array of pktinfo structures */
209 extern int sseqtbl[], rseqtbl[], sbufuse[], sacktbl[], wslots, winlo, wslotn,
210 sbufnum, rbufnum, pktpaus, reliable;
213 static int dontsend = 0;
214 extern int streaming;
215 #endif /* STREAMING */
217 extern int ttprty; /* from ck*tio.c */
220 extern int spsiz, spmax, rpsiz, timint, timef, npad, bestlen, maxsend;
221 extern int rpt, rptq, rptflg, capas, spsizf, en_fin, tsecs, flow;
222 extern int pktnum, sndtyp, rcvtyp, bctr, bctu, bctf, bctl, rsn, rln, maxtry;
223 extern int size, osize, maxsize, spktl, rpktl, nfils, stdouf, fsecs;
224 extern int turn, turnch, displa, pktlog, seslog, xflg, mypadn;
225 extern int hcflg, server, cxseen, czseen, discard, slostart;
226 extern int nakstate, quiet, success, xitsta, what, filestatus;
227 extern int spackets, rpackets, timeouts, retrans, crunched, urpsiz;
228 extern int carrier, fdispla, srvidl;
231 extern CKFLOAT fptsecs, fpfsecs, fpxfsecs;
234 extern long filcnt, filrej, speed, filcps, tfcps;
235 extern CK_OFF_T ffc, flci, flco, tlci, tlco, tfc;
237 extern char *cmarg, filnam[];
239 extern CHAR padch, mypadc, eol, seol, ctlq, sstate;
240 extern CHAR *recpkt, *data, myinit[];
241 extern CHAR *srvptr, stchr, mystch, *rdatap;
242 extern CHAR padbuf[];
243 extern CHAR * epktmsg;
244 extern int epktrcvd, epktsent;
246 #ifdef OS2 /* AUTODOWNLOAD parameters */
247 extern int adl_kmode, adl_zmode; /* Match Packet to signal download */
248 extern char * adl_kstr; /* KERMIT Download String */
249 extern char * adl_zstr; /* ZMODEM Download String */
253 CHAR ksbuf[96] = { NUL, NUL }; /* Autodownload "Kermit Start" buf */
254 #endif /* CK_AUTODL */
256 int numerrs = 0; /* Number of packet errors so far */
257 int rcvtimo = 0; /* Timeout for receiving a packet */
258 int idletmo = 0; /* Flag for idle timeout */
260 long filcps = 0L; /* CPS most recent file transferred */
261 long tfcps = 0L; /* CPS most recent transaction */
262 long xfsecs = 0L; /* Elapsed time for most recent file */
264 CKFLOAT fpxfsecs = 0.0; /* Ditto, but floating point */
268 int rrttbl[64], srttbl[64]; /* Packet timestamp tables */
270 #define RTT_SCALE 1000
272 rttsamples, /* Round trip time samples */
273 rttdelay, /* RTT delay */
274 pktintvl, /* Interpacket arrival time */
275 rttvariance, /* RTT variance */
276 rttstddev; /* RTT standard deviation */
277 #endif /* CK_TIMERS */
279 /* CRC generation tables */
281 long crcta[16] = { 0L, 010201L, 020402L, 030603L, 041004L,
282 051205L, 061406L, 071607L, 0102010L, 0112211L, 0122412L, 0132613L, 0143014L,
283 0153215L, 0163416L, 0173617L
286 long crctb[16] = { 0L, 010611L, 021422L, 031233L, 043044L,
287 053655L, 062466L, 072277L, 0106110L, 0116701L, 0127532L, 0137323L, 0145154L,
288 0155745L, 0164576L, 0174367L
293 Round-trip timer calculations adapted from Tim Kientzle's article,
294 "Improving Kermit Performance", Dr Dobb's Journal, February 1996.
298 /* R T T I N I T -- Initialize timers at start of transaction */
301 rttinit() { /* Initialize round-trip timing */
307 rttsamples = 0L; /* Samples (packets) */
308 rttvariance = 0L; /* Variance in delay */
309 rttdelay = (long) timint * RTT_SCALE; /* Delay */
310 pktintvl = (long) timint * RTT_SCALE; /* Delay */
311 rttstddev = (long) timint * RTT_SCALE; /* Standard deviation of delay */
313 /* Tables of timestamps indexed by packet sequence number */
315 for (i = 0; i < 64; i++) {
316 rrttbl[i] = -1; /* Time each packet was received */
317 srttbl[i] = -1; /* Time each packet was sent */
319 rcvtimo = timint; /* Initial timeout is what user said */
322 /* G E T R T T -- Get packet round trip time */
324 Call with nakstate == 0 if file sender, nonzero if receiver,
325 and n == packet sequence number of the packet we just received.
328 -1 on failure with rcvtimo set to timint (what the user said), or:
329 0 on success with rcvtimo set to dynamically calculated value:
330 1 <= rcvtimo <= timint * 3.
333 getrtt(nakstate, n) int nakstate, n; {
334 extern int mintime, maxtime;
335 static int prevz = 0, prevr = 0;
336 int x, y, yy, z = 0, zz = 0; /* How long did it take to get here? */
338 rcvtimo = timint; /* Default timeout is what user said */
340 if (timint == 0) /* We're not timing out. */
343 if (!rttflg) /* Not supposed to be doing this? */
344 return(-1); /* So don't */
346 if (!RTT_SCALE) /* Paranoia... */
349 /* rtimer() (reset timer) is not called until 1st data packet */
353 /* S (F [ A ] D* Z)* B */
355 /* NOTE: we calculate both the round-trip time AND the packet */
356 /* arrival rate. We don't use the RTT for anything, we just display it. */
357 /* Timeouts are based on the packet arrival rate. */
359 if (spackets > 3) { /* Don't start till 4th packet */
360 if (nakstate) { /* File receiver */
361 x = rrttbl[n]; /* Time when I got packet n */
362 y = rrttbl[n > 0 ? n - 1 : 63]; /* Time when I got packet n-1 */
363 yy = srttbl[n > 0 ? n - 1 : 63]; /* Time when I sent ACK(n-1) */
364 if (x > -1 && y > -1) { /* Be careful */
365 z = x - y; /* Packet rate */
366 zz = x - yy; /* Round trip time */
367 z++; /* So sender & receiver differ */
368 debug(F101,"RTT RECV","",z);
369 } else { /* This shouldn't happen */
370 debug(F101,"RTT RECV ERROR spackets","",spackets);
371 debug(F101,"RTT RECV ERROR sequence","",n);
374 } else { /* File sender */
375 x = rrttbl[n]; /* Time when I got ACK(n) */
376 y = rrttbl[n > 0 ? n - 1 : 63]; /* Time when I got packet n-1 */
377 yy = srttbl[n]; /* Time when I sent n */
378 if (x > -1 && y > -1) {
379 z = x - y; /* Packet rate */
380 zz = x - yy; /* Round trip time */
381 debug(F101,"RTT SEND","",z);
383 debug(F100,"RTT SEND ERROR","",0);
387 if (z < 1) /* For fast connections */
388 z = RTT_SCALE / 2; /* Convert to scale... */
391 debug(F101,"RTT z scaled","",z);
393 if (zz < 1) /* For fast connections */
394 zz = RTT_SCALE / 2; /* Convert to scale... */
398 rttdelay = zz; /* Round trip time of this packet */
401 This was used in C-Kermit 7.0 (and 6.0?) but not only is it overkill,
402 it also can produce ridiculously long timeouts under certain conditions.
403 Replaced in 8.0 by a far simpler and more aggressive strategy.
405 if (rttsamples++ == 0L) { /* First sample */
407 } else { /* Subsequent samples */
408 long oldavg = pktintvl;
411 if (rttsamples > 30) /* Use real average for first 30 */
412 rttsamples = 30; /* then decaying average. */
414 /* Average delay, difference squared, variance, std deviation */
416 pktintvl += (z - pktintvl) / rttsamples;
417 rttdiffsq = (z - oldavg) * (z - oldavg);
418 rttvariance += (rttdiffsq - rttvariance) / rttsamples;
419 debug(F101,"RTT stddev1","",rttstddev);
420 if (rttstddev < 1L) /* It can be zero, in which case */
421 rttstddev = RTT_SCALE / 3; /* set it to something small... */
422 rttstddev = (rttstddev + rttvariance / rttstddev) / 2;
424 debug(F101,"RTT stddev2","",rttstddev);
425 debug(F101,"RTT delay ","",pktintvl);
426 rcvtimo = (pktintvl + (3L * rttstddev)) / RTT_SCALE + 1;
427 if (rpackets < 32) /* Allow for slow start */
428 rcvtimo += rcvtimo + 2;
429 else if (rpackets < 64)
430 rcvtimo += rcvtimo / 2 + 1;
431 /* On a reliable link, don't try too hard to time out. */
432 /* Especially on fast local network connections. */
433 if (server && what == W_NOTHING) /* Server command wait */
434 rcvtimo = rcvtimo; /* == srvtim */
435 else if (reliable == SET_ON && rcvtimo > 0) /* Reliable */
436 rcvtimo = rcvtimo +15; /* and not server command wait */
437 else /* Not reliable or server cmd wait */
439 if (rcvtimo < mintime) /* Lower bound */
441 if (maxtime > 0) { /* User specified an upper bound */
442 if (rcvtimo > maxtime)
444 } else if (maxtime == 0) { /* User didn't specify */
445 if (rcvtimo > timint * 6)
446 rcvtimo = timint * 6;
452 x = (CKFLOAT)(prevz + z + z) / 3.0;
453 rcvtimo = (int)((((CKFLOAT)x * 2.66) / RTT_SCALE) + 0.5);
454 debug(F101,"RTT rcvtimo (float)","",rcvtimo);
457 rcvtimo = (prevz + z + z) / RTT_SCALE;
458 debug(F101,"RTT rcvtimo (int)","",rcvtimo);
462 zz = (rttdelay + 500) / 1000;
463 if (rcvtimo > (zz * 3))
469 if (rcvtimo < mintime) /* Lower bound */
472 if (maxtime > 0) { /* Upper bound */
473 if (rcvtimo > maxtime)
476 if (rcvtimo == (prevr - 1))
479 debug(F101,"RTT final rcvtimo","",rcvtimo);
485 #endif /* CK_TIMERS */
487 /* I N P U T -- Attempt to read packet number 'pktnum'. */
490 This is the function that feeds input to Kermit's finite state machine,
491 in the form of a character in the range 32-126, normally a packet type
492 (uppercase letter) or pseudo-packet-type (lowercase letter).
494 If a special start state is in effect, that state is returned as if it were
495 the type of an incoming packet.
499 int type = 0, acktype; /* Received packet type */
500 int x, y, k; /* Workers */
501 int z, pi, nf; /* Worker, packet index, NAK flag */
504 debug(F000,"input sstate","",sstate);
505 debug(F101,"input nakstate","",nakstate);
506 debug(F000,"input sndtyp","",sndtyp);
507 debug(F101,"input xitsta","",xitsta);
508 debug(F101,"input what","",what);
510 while (1) { /* Big loop... */
512 It is ttchk()'s responsibility to tell us if the connection is broken,
513 and to do so instantly and nondestructively -- no blocking, etc, that would
514 slow down file transfer.
517 debug(F100,"input CONNECTION BROKEN","",0);
521 if (sstate != 0) { /* If a start state is in effect, */
522 type = sstate; /* return it like a packet type, */
523 sstate = 0; /* and then nullify it. */
524 numerrs = 0; /* (PWP) no errors so far */
527 if (nakstate) { /* This section for file receiver. */
528 if (wslots > 1) { /* If we're doing windows, */
529 x = rseqtbl[winlo]; /* see if desired packet already in. */
530 debug(F101,"input winlo","",winlo);
531 debug(F101,"input rseqtbl[winlo]","",rseqtbl[winlo]);
532 if (x > -1) { /* Already there? */
533 if (r_pkt[x].pk_seq == winlo) { /* (double check) */
534 rsn = winlo; /* Yes, return its info */
535 debug(F101,"input return pre-stashed packet","",rsn);
537 rdatap = r_pkt[x].pk_adr; /* like rpack would do. */
538 rln = (int)strlen((char *) rdatap);
539 type = r_pkt[x].pk_typ;
544 type = rpack(); /* Try to read a packet. */
545 debug(F101,"input rpack","",type);
547 while (type == 'e') { /* Handle echoes */
548 debug(F101,"input echo discarded","",type);
554 debug(F011,"input type D=",(char *)rdatap,39);
556 debug(F000,"input type",(char *)rdatap,type);
562 errpkt((CHAR *)"User cancelled.");
566 #endif /* OLDCHKINT */
570 "FAILED - Interrupted" :
571 "FAILED - Connection lost";
573 xxscreen(SCR_PT,'q',0L,s);
575 return('q'); /* Ctrl-C or connection lost */
577 if (type < 0) { /* Receive window full */
578 /* Another thing to do here would be to delete */
579 /* the highest packet and NAK winlo. But that */
580 /* shouldn't be necessary since the other Kermit */
581 /* should not have sent a packet outside the window. */
584 ckmakxmsg(foo,256,"Receive window full (rpack): wslots=",
585 ckitoa(wslots)," winlo=",ckitoa(winlo)," pktnum=",
586 ckitoa(pktnum), NULL,NULL,NULL,NULL,NULL,NULL);
588 debug(F100,foo,"",0);
590 errpkt((CHAR *)"Receive window full");
591 debug(F101,"rpack receive window full","",0);
592 debug(F101," wslots","",wslots);
593 debug(F101," winlo","",winlo);
594 debug(F101," pktnum","",pktnum);
603 if (chkint() < 0) { /* Check for console interrupts. */
604 errpkt((CHAR *)"User cancelled."); /* (old way) */
608 #endif /* OLDCHKINT */
611 if (streaming) { /* Streaming */
612 if (type == 'Q' || type == 'T') { /* Errors are fatal. */
613 crunched++; /* For statistics */
614 errpkt((CHAR *)"Transmission error on reliable link.");
618 #endif /* STREAMING */
620 debug(F101,"input got E, nakstate","",nakstate);
621 break; /* Error packet */
623 if (type == 'Q') { /* Crunched packet. */
627 Packet arrived damaged. It was most likely the packet we were expecting
628 next, so we send a NAK for that packet. Prior to 5A(189), we always
629 NAK'd winlo here, but that was bad because if two (or more) different
630 packets were damaged, we would keep NAKing the first one and never NAK the
631 other ones, which could result in a lengthy series of timeouts. Now we
632 NAK the oldest as-yet-unNAK'd missing packet.
635 rcvtimo++; /* Stretch the timeout a little */
636 #endif /* CK_TIMERS */
637 z = (winlo + wslots) % 64; /* Search from winlo to z */
638 debug(F101,"ZZZ crunched z","",z);
639 nf = 0; /* NAK flag not set yet */
640 for (x = winlo; x != z; x = (x + 1) % 64) {
641 debug(F101,"ZZZ x","",x);
642 if (rseqtbl[x] > -1) /* Have I received packet x? */
643 continue; /* Yes, go on. */
644 debug(F101,"ZZZ x not recd yet","",x);
645 pi = sseqtbl[x]; /* No, have I NAK'd it yet? */
646 if (pi < 0 || s_pkt[pi].pk_rtr == 0) {
647 debug(F101,"ZZZ x not NAK'd yet","",x);
648 nack(x); /* No, NAK it now. */
649 nf = 1; /* Flag that I did. */
653 if (!nf) { /* If we didn't NAK anything above, */
654 debug(F101,"ZZZ NAKing winlo","",winlo);
655 if (nack(winlo) < 0) { /* we have to NAK winlo (again) */
656 errpkt((CHAR *)"Too many retries."); /* Too many */
664 if (type == 'T') { /* Timeout */
666 /* K95 does this its own way */
667 if (server && srvidl) {
669 debug(F101,"SERVER IDLE TIMEOUT","",srvidl);
674 rcvtimo++; /* Stretch the timeout a little */
675 #endif /* CK_TIMERS */
677 debug(F101,"input receive-state timeout, winlo","",winlo);
678 /* NAK only the packet at window-low */
679 debug(F101,"input sending NAK for winlo","",winlo);
681 if (x > 0) /* Don't give up if there is still */
682 continue; /* something to read. */
686 return('q'); /* Connection Lost */
688 if (nack(winlo) < 0) {
689 debug(F101,"input sent too many naks","",winlo);
690 errpkt((CHAR *)"Too many retries.");
695 if (rsn == winlo) { /* Got the packet we want, done. */
697 if (rttflg && timint) /* Dynamic round trip timers? */
698 getrtt(nakstate, rsn); /* yes, do it. */
699 else /* JHD 20100208 */
700 rcvtimo = timint; /* JHD 20100208 */
701 #endif /* CK_TIMERS */
702 debug(F101,"input rsn=winlo","",rsn);
706 /* Got a packet out of order. */
708 debug(F101,"input out of sequence, rsn","",rsn);
709 k = rseqtbl[rsn]; /* Get window slot of this packet. */
710 debug(F101,"input rseqtbl[rsn]","",k);
712 debug(F101,"input recv can't find index for rcvd pkt","",rsn);
713 /* Was "Internal error 21" */
714 /* This should not happen */
715 errpkt((CHAR *)"Sliding windows protocol error.");
719 y = chkwin(rsn,winlo,wslots); /* See what window it's in. */
720 debug(F101,"input recv chkwin","",y);
721 if (y == 1) { /* From previous window. */
723 if (!streaming) /* NO RESEND IF STREAMING! */
724 #endif /* STREAMING */
725 resend(rsn); /* Resend the ACK (might have data) */
726 freerpkt(rsn); /* Get rid of received packet */
727 continue; /* Back to wait for another packet */
728 } else { /* In this window or out of range */
729 if (y < 0) /* If out of range entirely, */
730 freerpkt(rsn); /* release its buffer */
733 if (streaming) { /* Streaming (this shouldn't happen) */
734 errpkt((CHAR *)"Sequence error on reliable link.");
738 #endif /* STREAMING */
740 /* If our receive window is full, NAK window-low */
742 if (rbufnum < 1) { /* Receive window full? */
743 if (nack(winlo) < 0) { /* No choice, must NAK winlo. */
744 errpkt((CHAR *)"Too many retries."); /* Too many */
750 Receive window not full. This is a packet in the current window but it is
751 not the desired packet at winlo. So therefore there are gaps before this
752 packet. So we find the "lowest" unNAK'd missing packet, if any, between
753 winlo and this one, and NAK it. If there are no as-yet-unNAK'd missing
754 packets in the window, then we send nothing and go wait for another packet.
755 In theory, this could result in a timeout, but in practice it is likely that
756 the already-NAK'd missing packets are already on their way. Note, we do not
757 NAK ahead of ourselves, as that only creates unnecessary retransmissions.
759 for (x = winlo; x != rsn; x = (x + 1) % 64) {
760 if (rseqtbl[x] > -1) /* Have I received packet x? */
761 continue; /* Yes, check next sequence number. */
762 pi = sseqtbl[x]; /* No, have I NAK'd it yet? */
763 if (pi < 0 || s_pkt[pi].pk_rtr == 0) {
764 nack(x); /* No, NAK it now. */
770 } else { /* Otherwise file sender... */
773 if (streaming && sndtyp == 'D') {
774 debug(F101,"STREAMING input streaming","",streaming);
775 debug(F000,"STREAMING input sndtyp","",sndtyp);
777 type = 'Y'; /* Pretend we got an ACK */
779 #endif /* STREAMING */
780 if (!nak2ack) { /* NAK(n+1) = ACK(n) */
781 if (wslots > 1) { /* Packet at winlo already ACK'd? */
782 if (sacktbl[winlo]) { /* If so, */
783 sacktbl[winlo] = 0; /* Turn off the ACK'd flag */
784 winlo = (winlo + 1) % 64; /* Rotate the window */
785 type = 'Y'; /* And return ACK */
787 "input send returning pre-stashed ACK","",
793 if (!(streaming && sndtyp == 'D')) { /* Not streaming | data */
794 type = rpack(); /* Try to read an acknowledgement */
795 } else { /* Streaming and in Data phase */
796 type = 'Y'; /* Assume all is normal */
797 if (chkint() < 0) /* Check for console interrupts. */
799 else if (ttchk() > 4 + bctu) /* Check for return traffic */
801 debug(F000,"input streaming type","",type);
803 #endif /* STREAMING */
804 debug(F111,"input send",(char *) rdatap,(int) type);
805 while (type == 'e') { /* Handle echoes */
806 debug(F000,"echo discarded","",type);
812 errpkt((CHAR *)"User cancelled.");
816 #endif /* OLDCHKINT */
818 xxscreen(SCR_PT,'q',0L,
819 ((char *)((type == -2) ?
825 return('q'); /* Ctrl-C or connection lost */
831 "Receive window full (error 18): wslots=",
833 " winlo=",ckitoa(winlo)," pktnum=",
834 ckitoa(pktnum), NULL,NULL,NULL,NULL,NULL,NULL);
836 debug(F100,foo,"",0);
838 errpkt((CHAR *)"Receive window full"); /* was "internal */
839 debug(F101," wslots","",wslots); /* error 18" */
840 debug(F101," winlo","",winlo);
841 debug(F101," pktnum","",pktnum);
847 dumprbuf(); /* Debugging */
850 if (chkint() < 0) { /* Check for console interrupts. */
851 errpkt((CHAR *)"User cancelled.");
854 #endif /* OLDCHKINT */
859 if (streaming) { /* Streaming */
860 if (type == 'Q' || type == 'T') { /* Errors are fatal. */
861 crunched++; /* For statistics */
862 errpkt((CHAR *)"Transmission error on reliable link.");
866 #endif /* STREAMING */
868 debug(F101,"input send got E, nakstate","",nakstate);
869 break; /* Error packet */
871 if (type == 'Q') { /* Crunched packet */
872 crunched++; /* For statistics */
873 numerrs++; /* For packet resizing */
874 x = resend(winlo); /* Resend window-low */
877 errpkt((CHAR *)"Too many retries");
882 if (type == 'T') { /* Timeout waiting for ACKs. */
883 timeouts++; /* Count it */
884 numerrs++; /* Count an error too */
885 debug(F101,"input send state timeout, winlo","",winlo);
887 /* Retransmit the oldest un-ACK'd packet. */
889 debug(F101,"input send resending winlo","",winlo);
890 if (resend(winlo) < 0) { /* Check retries */
891 debug(F101,"input send too many resends","",maxtry);
892 errpkt((CHAR *)"Too many retries");
896 /* Reduce prevailing packet length */
897 x = sseqtbl[winlo]; /* Get length of packet we want ACKd */
898 if (x > -1) { /* Only if we have a valid index */
899 if (s_pkt[x].pk_typ == 'D') { /* only for D packets */
900 spsiz = (s_pkt[x].pk_len + 8) >> 1; /* halve it */
901 if (spsiz < 20) spsiz = 20; /* within reason */
902 debug(F101,"input T cut packet length","",spsiz);
909 /* Got an actual normal packet */
911 nak2ack = 0; /* Unset this flag. */
912 y = chkwin(rsn,winlo,wslots); /* Is it in the window? */
913 debug(F101,"input send rsn","",rsn);
914 debug(F101,"input send winlo","",winlo);
915 debug(F101,"input send chkwin","",y);
917 if (type == 'Y') { /* Got an ACK */
918 if (y == 0) { /* In current window */
919 if (spackets < 4) /* Error counter doesn't count */
920 numerrs = 0; /* until data phase. */
921 sacktbl[rsn]++; /* Mark the packet as ACK'd */
922 x = sseqtbl[rsn]; /* Get ACK'd packet's buffer index */
923 debug(F101,"bestlen ack x","",x);
926 acktype = s_pkt[x].pk_typ; /* Get type */
927 debug(F000,"bestlen ack type","",acktype);
929 if (acktype == 'D') { /* Adjust data packet length */
930 if (spsiz > bestlen) {
932 debug(F101,"bestlen B","",bestlen);
936 debug(F101,"bestlen retry","",s_pkt[x].pk_rtr);
937 debug(F101,"bestlen len","",s_pkt[x].pk_len);
938 debug(F101,"bestlen spackets","",spackets);
941 /* Set new best length */
942 if (s_pkt[x].pk_rtr == 0 &&
943 s_pkt[x].pk_len + 8 > bestlen) {
944 bestlen = s_pkt[x].pk_len + 8;
947 debug(F101,"bestlen A","",bestlen);
951 debug(F101,"bestlen wslots","",wslots);
952 debug(F101,"bestlen maxsend","",maxsend);
957 (maxsend <= spmax) &&
961 debug(F101,"bestlen spsiz A","",spsiz);
963 /* Creep up to best length */
964 } else if ((spackets > 5) &&
965 (spsiz < bestlen - 8)) {
966 spsiz += (bestlen - spsiz) / 3;
967 debug(F101,"bestlen spsiz B","",spsiz);
969 /* Push the envelope */
970 } else if ((spackets % (wslots + 1) == 0) &&
972 (bestlen < spmax - 8) &&
974 spsiz += (spmax - bestlen) / 3;
975 debug(F101,"bestlen spsiz C","",spsiz);
977 /* But not too far */
980 debug(F101,"bestlen spsiz D","",spsiz);
987 if (rttflg && timint) /* If doing dynamic timers */
988 getrtt(nakstate, rsn); /* call routine to set it. */
989 else /* JHD 20100208 */
990 rcvtimo = timint; /* JHD 20100208 */
991 #endif /* CK_TIMERS */
993 NOTE: The following statement frees the buffer of the ACK we just got.
994 But the upper layers still need the data, like if it's the ACK to an I,
995 S, F, D, Z, or just about any kind of packet. So for now, freerbuf()
996 deallocates the buffer, but does not erase the data or destroy the pointer
997 to it. There's no other single place where these receive buffers can be
998 correctly freed (?) ...
1000 freerpkt(rsn); /* Free the ACK's buffer */
1001 freesbuf(rsn); /* *** Free the sent packet's buffer */
1002 if (rsn == winlo) { /* Got the one we want */
1004 winlo = (winlo + 1) % 64;
1005 debug(F101,"input send rotated send window","",winlo);
1006 break; /* Return the ACK */
1008 debug(F101,"input send mark pkt","",rsn);
1009 continue; /* Otherwise go read another packet */
1011 } else if (y == 1 && wslots < 2) { /* (190) ACK for previous */
1012 numerrs++; /* == NAK for current, count error */
1013 debug(F101,"input send ACK for previous","",rsn);
1014 freerpkt(rsn); /* Free NAK's buffer */
1015 x = resend(winlo); /* Resend current packet */
1018 errpkt((CHAR *)"Too many retries");
1020 } else continue; /* Resend ok, go read another packet */
1021 } else { /* Other cases, just ignore */
1022 debug(F101,"input send ACK out of window","",rsn);
1027 if (type == 'N') { /* NAK */
1028 numerrs++; /* Count an error */
1030 if (streaming) { /* Streaming */
1031 errpkt((CHAR *)"NAK received on reliable link.");
1035 #endif /* STREAMING */
1037 debug(F101,"input send NAK","",rsn);
1039 /* Reduce prevailing packet length */
1040 x = sseqtbl[rsn]; /* Length of packet that was NAK'd */
1041 if (x > -1) { /* If it's a Data packet we've sent */
1042 if (s_pkt[x].pk_typ == 'D') {
1043 spsiz = (s_pkt[x].pk_len + 8) >> 1; /* Halve length */
1045 /* This might be a good idea -- haven't tried it ... */
1046 if (bestlen > 0 && spsiz > bestlen)
1048 #endif /* COMMENT */
1049 if (spsiz < 20) spsiz = 20;
1050 debug(F101,"input N cut packet length","",spsiz);
1054 freerpkt(rsn); /* Free buffer where NAK lies. */
1055 if (y == 0) { /* In current window */
1056 debug(F100," in window","",0);
1057 k = sseqtbl[rsn]; /* Get pointer to NAK'd packet. */
1058 if (k < 0 || (k > -1 && s_pkt[k].pk_typ == ' ')) {
1059 x = resend(winlo); /* Packet we haven't sent yet. */
1061 x = resend(rsn); /* Resend requested packet. */
1063 if (x < 0) { /* Resend error is fatal. */
1065 errpkt((CHAR *)"Too many retries");
1067 } else continue; /* Resend ok, go read another packet */
1068 } else if ((rsn == (pktnum + 1) % 64)) { /* NAK for next pkt */
1070 debug( F101,"NAK for next packet, windowing","",rsn);
1071 x = resend(winlo); /* Resend window-low */
1074 errpkt((CHAR *)"Too many retries");
1077 continue; /* Go back and read another pkt */
1079 debug(F101,"NAK for next packet, no windowing","",rsn);
1080 x = (rsn == 0) ? 63 : rsn - 1;
1081 if (x == 0 && (sndtyp == 'S' || sndtyp == 'I')) {
1082 resend(0); /* ACK for S or I packet missing */
1083 continue; /* so resend the S or I */
1085 rsn = x; /* Else, treat NAK(n+1) as ACK(n) */
1086 nak2ack = 1; /* Go back and process the ACK */
1088 } else if (y > 0) { /* NAK for pkt we can't resend */
1089 debug(F101," NAK out of window","",rsn); /* bad... */
1091 errpkt((CHAR *)"NAK out of window");
1093 } else continue; /* Ignore other NAKs */
1094 } /* End of file-sender NAK handler */
1096 if (rsn == winlo) { /* Not ACK, NAK, timeout, etc. */
1097 debug(F000,"input send unexpected type","",type);
1100 } /* End of file-sender section */
1101 } /* End of input() while() loop */
1103 When the window size is 1 and we have the packet we want, there can not
1104 possibly be anything waiting for us on the connection that is useful to us.
1105 However, there might be redundant copies of a packet we already got, which
1106 would cause needless cycles of repeated packets. Therefore we flush the
1107 communications input buffer now to try to get rid of undesired and unneeded
1108 packets that we have not read yet.
1110 Actually, the first sentence above is not entirely true: there could be an
1111 Error packet waiting to be read. Flushing an E packet is bad because it
1112 will not be resent, and we'll go into a cycle of timing out and
1113 retransmitting up to the retry limit. - fdc 2007/03/02
1115 if (wslotn == 1 /* (not wslots!) */
1117 && !streaming /* But not when streaming */
1118 #endif /* STREAMING */
1120 debug(F100,"input about to flush","",0);
1121 ttflui(); /* Got what we want, clear input buffer. */
1124 if (!nakstate) /* When sending */
1125 rcalcpsz(); /* recalculate size every packet */
1128 xitsta |= (what ? what : 1); /* Remember what failed. */
1129 debug(F101,"input winlo","",winlo);
1130 debug(F101,"input rsn","",rsn);
1131 debug(F000,"input returning type","",type);
1132 return(rcvtyp = type); /* Success, return packet type. */
1136 /* P A R C H K -- Check if Kermit packet has parity */
1139 Call with s = pointer to packet, start = packet start character, n = length.
1140 Returns 0 if packet has no parity, -1 on error, or, if packet has parity:
1141 'e' for even, 'o' for odd, 'm' for mark. Space parity cannot be sensed.
1142 So a return value of 0 really means either space or none.
1143 Returns -2 if parity has already been checked during this protocol operation.
1147 parchk(CHAR *s, CHAR start, int n)
1149 parchk(s,start,n) CHAR *s, start; int n;
1150 #endif /* CK_ANSIC */
1152 CHAR s0, s1, s2, s3;
1154 debug(F101,"parchk n","",n);
1155 debug(F101,"parchk start","",start);
1157 s0 = s[0] & 0x7f; /* Mark field (usually Ctrl-A) */
1159 if (s0 != start || n < 5) return(-1); /* Not a valid packet */
1161 /* Look at packet control fields, which never have 8th bit set */
1162 /* First check for no parity, most common case. */
1164 if (((s[0] | s[1] | s[2] | s[3]) & 0x80) == 0)
1165 return(0); /* No parity or space parity */
1167 /* Check for mark parity */
1169 if (((s[0] & s[1] & s[2] & s[3]) & 0x80) == 0x80)
1170 return('m'); /* Mark parity */
1172 /* Packet has some kind of parity */
1173 /* Make 7-bit copies of control fields */
1175 s1 = s[1] & 0x7f; /* LEN */
1176 s2 = s[2] & 0x7f; /* SEQ */
1177 s3 = s[3] & 0x7f; /* TYPE */
1179 /* Check for even parity */
1181 if ((s[0] == p_tbl[s0]) &&
1182 (s[1] == p_tbl[s1]) &&
1183 (s[2] == p_tbl[s2]) &&
1184 (s[3] == p_tbl[s3]))
1187 /* Check for odd parity */
1189 if ((s[0] != p_tbl[s0]) &&
1190 (s[1] != p_tbl[s1]) &&
1191 (s[2] != p_tbl[s2]) &&
1192 (s[3] != p_tbl[s3]))
1195 /* Otherwise it's probably line noise. Let checksum calculation catch it. */
1199 #endif /* PARSENSE */
1202 Check to make sure timeout intervals are long enough to allow maximum
1203 length packets to get through before the timer goes off. If not, the
1204 timeout interval is adjusted upwards.
1206 This routine is called at the beginning of a transaction, before we
1207 know anything about the delay characteristics of the line. It works
1208 only for serial communication devices; it trusts the speed reported by
1209 the operating system.
1211 Call with a timout interval. Returns it, adjusted if necessary.
1214 chktimo(timo,flag) int timo, flag; {
1215 long cps, z; int x, y;
1217 debug(F101,"chktimo streaming","",streaming);
1220 #endif /* STREAMING */
1222 debug(F101,"chktimo timo","",timo); /* Timeout before adjustment */
1223 debug(F101,"chktimo flag","",flag);
1225 if (flag) /* Don't change timeout if user */
1226 return(timo); /* gave SET SEND TIMEOUT command. */
1227 debug(F101,"chktimo spmax","",spmax);
1228 debug(F101,"chktimo urpsiz","",urpsiz);
1230 if (!network) { /* On serial connections... */
1231 speed = ttgspd(); /* Get current speed. */
1233 cps = speed / 10L; /* Convert to chars per second */
1235 long plen; /* Maximum of send and rcv pkt size */
1236 z = cps * (long) timo; /* Chars per timeout interval */
1237 z -= z / 10L; /* Less 10 percent */
1239 if (urpsiz > spmax) plen = urpsiz;
1240 debug(F101,"chktimo plen","",plen);
1241 if (z < plen) { /* Compare with packet size */
1242 x = (int) ((long) plen / cps); /* Adjust if necessary */
1243 y = x / 10; /* Add 10 percent for safety */
1244 if (y < 2) y = 2; /* Or 2 seconds, whichever is more */
1246 if (x > timo) /* If this is greater than current */
1247 timo = x; /* timeout, change the timeout */
1248 debug(F101,"chktimo new timo","",timo);
1256 /* S P A C K -- Construct and send a packet */
1259 spack() sends a packet of the given type, sequence number n, with len data
1260 characters pointed to by d, in either a regular or extended- length packet,
1261 depending on len. Returns the number of bytes actually sent, or else -1
1262 upon failure. Uses global npad, padch, mystch, bctu, data. Leaves packet
1263 fully built and null-terminated for later retransmission by resend().
1264 Updates global sndpktl (send-packet length).
1266 NOTE: The global pointer "data" is assumed to point into the 7th position
1267 of a character array (presumably in packet buffer for the current packet).
1268 It was used by getpkt() to build the packet data field. spack() fills in
1269 the header to the left of the data pointer (the data pointer is defined
1270 in getsbuf() in ckcfn3.c). If the address "d" is the same as "data", then
1271 the packet's data field has been built "in place" and need not be copied.
1275 spack(char pkttyp, int n, int len, CHAR *d)
1277 spack(pkttyp,n,len,d) char pkttyp; int n, len; CHAR *d;
1278 #endif /* CK_ANSIC */
1281 int ix, j, k, x, lp, longpkt, copy, loglen;
1284 CKFLOAT t1 = 0.0, t2 = 0.0;
1285 #endif /* GFTIMER */
1287 register CHAR *cp, *mydata;
1290 copy = (d != data); /* Flag whether data must be copied */
1293 if (deblog) { /* Save lots of function calls! */
1294 debug(F101,"spack n","",n);
1296 if (pkttyp != 'D') { /* Data packets would be too long */
1297 debug(F111,"spack data",data,data);
1298 debug(F111,"spack d",d,d);
1300 #endif /* COMMENT */
1301 debug(F101,"spack len","",len);
1302 debug(F101,"spack copy","",copy);
1306 longpkt = (len + bctl + 2) > 94; /* Decide whether it's a long packet */
1307 mydata = data - 7 + (longpkt ? 0 : 3); /* Starting position of header */
1308 k = sseqtbl[n]; /* Packet structure info for pkt n */
1311 if (deblog) { /* Save 2 more function calls... */
1312 debug(F101,"spack mydata","",mydata);
1313 debug(F101,"spack sseqtbl[n]","",k);
1317 #endif /* STREAMING */
1318 debug(F101,"spack sending packet out of window","",n);
1322 #endif /* COMMENT */
1324 s_pkt[k].pk_adr = mydata; /* Remember address of packet. */
1325 s_pkt[k].pk_seq = n; /* Record sequence number */
1326 s_pkt[k].pk_typ = pkttyp; /* Record packet type */
1328 spktl = 0; /* Initialize length of this packet */
1329 i = 0; /* and position in packet. */
1331 /* Now fill the packet */
1333 mydata[i++] = mystch; /* MARK */
1334 lp = i++; /* Position of LEN, fill in later */
1336 mydata[i++] = tochar(n); /* SEQ field */
1337 mydata[i++] = pkttyp; /* TYPE field */
1338 j = len + bctl; /* Length of data + block check */
1339 if (longpkt) { /* Long packet? */
1340 int x; /* Yes, work around SCO Xenix/286 */
1343 #endif /* CKTUNING */
1344 x = j / 95; /* compiler bug... */
1345 mydata[lp] = tochar(0); /* Set LEN to zero */
1346 mydata[i++] = tochar(x); /* Extended length, high byte */
1347 mydata[i++] = tochar(j % 95); /* Extended length, low byte */
1349 /* Header checksum - skip the function calls and loops */
1350 chk = (unsigned) mydata[lp] +
1351 (unsigned) mydata[lp+1] +
1352 (unsigned) mydata[lp+2] +
1353 (unsigned) mydata[lp+3] +
1354 (unsigned) mydata[lp+4] ;
1355 mydata[i++] = tochar((CHAR) ((((chk & 0300) >> 6) + chk) & 077));
1357 mydata[i] = '\0'; /* Terminate for header checksum */
1358 mydata[i++] = tochar(chk1(mydata+lp,5));
1359 #endif /* CKTUNING */
1360 } else mydata[lp] = tochar(j+2); /* Normal LEN */
1362 When sending a file, the data is already in the right place. If it weren't,
1363 it might make sense to optimize this section by using memcpy or bcopy
1364 (neither of which are portable), but only if our packets were rather long.
1365 When receiving, we're only sending ACKs so it doesn't matter. So count the
1366 following loop as a sleeping dog.
1368 if (copy) /* Data field built in place? */
1369 for ( ; len--; i++) mydata[i] = *d++; /* No, must copy. */
1370 else /* Otherwise, */
1371 i += len; /* Just skip past data field. */
1372 mydata[i] = '\0'; /* Null-terminate for checksum calc. */
1374 switch (bctu) { /* Block check */
1375 case 1: /* 1 = 6-bit chksum */
1376 ix = i - lp; /* Avoid "order of operation" error */
1377 mydata[i++] = tochar(chk1(mydata+lp,ix));
1379 case 2: /* 2 = 12-bit chksum */
1380 j = chk2(mydata+lp,i-lp);
1381 mydata[i++] = (unsigned)tochar((j >> 6) & 077);
1382 mydata[i++] = (unsigned)tochar(j & 077);
1384 case 3: /* 3 = 16-bit CRC */
1385 crc = chk3(mydata+lp,i-lp);
1386 mydata[i++] = (unsigned)tochar(((crc & 0170000)) >> 12);
1387 mydata[i++] = (unsigned)tochar((crc >> 6) & 077);
1388 mydata[i++] = (unsigned)tochar(crc & 077);
1390 case 4: /* 2 = 12-bit chksum, blank-free */
1391 j = chk2(mydata+lp,i-lp);
1393 (unsigned)(tochar((unsigned)(((j >> 6) & 077) + 1)));
1394 mydata[i++] = (unsigned)(tochar((unsigned)((j & 077) + 1)));
1398 mydata[i++] = seol; /* End of line (packet terminator) */
1401 If TELNET connection and packet terminator is carriage return,
1402 we must stuff either LF or NUL, according to SET TELNET NEWLINE-MODE
1403 (tn_nlm), to meet the TELNET NVT specification, unless user said RAW.
1405 If NEWLINE-MODE is set to LF instead of CR, we still send CR-NUL
1406 on a NVT connection and CR on a binary connection.
1411 #endif /* STREAMING */
1412 ((network && ttnproto == NP_TELNET) || (!local && sstelnet))
1414 switch (TELOPT_ME(TELOPT_BINARY) ? tn_b_nlm : tn_nlm) {
1415 case TNL_CR: /* NVT or BINARY */
1425 #endif /* TCPSOCKET */
1426 mydata[i] = '\0'; /* Terminate string */
1430 #endif /* STREAMING */
1432 ) /* Save a function call! */
1433 logpkt('s',n,mydata,loglen); /* Log the packet */
1435 /* (PWP) add the parity quickly at the end */
1438 case 'e': /* Even */
1439 for (cp = &mydata[i-1]; cp >= mydata; cp--)
1442 case 'm': /* Mark */
1443 for (cp = &mydata[i-1]; cp >= mydata; cp--)
1447 for (cp = &mydata[i-1]; cp >= mydata; cp--)
1448 *cp = p_tbl[*cp] ^ 128;
1450 case 's': /* Space */
1451 for (cp = &mydata[i-1]; cp >= mydata; cp--)
1456 if (pktpaus) msleep(pktpaus); /* Pause if requested */
1464 #endif /* STREAMING */
1465 x = ttol(padbuf,npad); /* Send any padding */
1471 srttbl[n > 0 ? n-1 : 63] = gtimer();
1473 srttbl[n] = gtimer();
1475 #endif /* CK_TIMERS */
1476 spktl = i; /* Remember packet length */
1478 s_pkt[k].pk_len = spktl; /* also in packet info structure */
1483 This code shows (in the debug log) how long it takes write() to execute.
1484 Sometimes on a congested TCP connection, it can surprise you -- 90 seconds
1490 #endif /* STREAMING */
1494 #endif /* GFTIMER */
1499 debug(F000,"STREAMING spack skipping","",pkttyp);
1502 #endif /* STREAMING */
1503 x = ttol(mydata,spktl); /* Send the packet */
1507 #endif /* STREAMING */
1508 debug(F101,"spack spktl","",spktl);
1509 debug(F101,"spack ttol returns","",x);
1510 if (x < 0) { /* Failed. */
1511 if (local && x < -1) {
1512 xxscreen(SCR_ST,ST_ERR,0L,"FAILED: Connection lost");
1513 /* We can't send an E packet because the connection is lost. */
1514 epktsent = 1; /* So pretend we sent one. */
1515 fatalio = 1; /* Remember we got a fatal i/o error */
1517 ckstrncpy((char *)epktmsg,"Connection lost",PKTMSGLEN);
1521 if (spktl > maxsend) /* Keep track of longest packet sent */
1525 if (deblog) { /* Log elapsed time for write() */
1527 debug(F101,"spack ttol msec","",(long)((t2-t1)*1000.0));
1529 #endif /* GFTIMER */
1533 #endif /* STREAMING */
1535 sndtyp = pkttyp; /* Remember packet type for echos */
1537 if (!dontsend) { /* If really sent, */
1538 spackets++; /* count it. */
1539 flco += spktl; /* Count the characters */
1540 tlco += spktl; /* for statistics... */
1542 if (deblog) { /* Save two function calls! */
1543 dumpsbuf(); /* Dump send buffers to debug log */
1544 debug(F111,"spack calling screen, mydata=",mydata,n);
1548 #endif /* STREAMING */
1551 if (fdispla != XYFD_N) x = 1;
1552 if ((fdispla == XYFD_B) && (pkttyp == 'D' || pkttyp == 'Y')) x = 0;
1554 xxscreen(SCR_PT,pkttyp,(long)n,(char *)mydata); /* Update screen */
1556 return(spktl); /* Return length */
1559 /* C H K 1 -- Compute a type-1 Kermit 6-bit checksum. */
1562 chk1(pkt,len) register CHAR *pkt; register int len; {
1563 register unsigned int chk;
1566 register unsigned int m; /* Avoid function call */
1567 m = (parity) ? 0177 : 0377;
1568 for (chk = 0; len-- > 0; pkt++)
1572 while (len-- > 0) chk += (unsigned) *pkt++;
1573 #endif /* COMMENT */
1575 chk = chk2(pkt,len);
1576 #endif /* CKTUNING */
1577 chk = (((chk & 0300) >> 6) + chk) & 077;
1578 debug(F101,"chk1","",chk);
1582 /* C H K 2 -- Compute the numeric sum of all the bytes in the packet. */
1585 chk2(pkt,len) register CHAR *pkt; register int len; {
1588 register unsigned int m;
1589 m = (parity) ? 0177 : 0377;
1590 for (chk = 0; len-- > 0; pkt++)
1593 /* Parity has already been stripped */
1595 while (len-- > 0) chk += (unsigned) *pkt++;
1596 #endif /* COMMENT */
1597 debug(F101,"chk2","",(unsigned int) (chk & 07777));
1598 return((unsigned int) (chk & 07777));
1601 /* C H K 3 -- Compute a type-3 Kermit block check. */
1603 Calculate the 16-bit CRC-CCITT of a null-terminated string using a lookup
1604 table. Assumes the argument string contains no embedded nulls.
1608 chk3(pkt,parity,len) register CHAR *pkt; int parity; register int len; {
1609 register long c, crc;
1610 register unsigned int m;
1611 m = (parity) ? 0177 : 0377;
1612 for (crc = 0; len-- > 0; pkt++) {
1613 c = crc ^ (long)(*pkt & m);
1614 crc = (crc >> 8) ^ (crcta[(c & 0xF0) >> 4] ^ crctb[c & 0x0F]);
1616 return((unsigned int) (crc & 0xFFFF));
1620 chk3(pkt,len) register CHAR *pkt; register int len; {
1621 register long c, crc;
1622 for (crc = 0; len-- > 0; pkt++) {
1623 c = crc ^ (long)(*pkt);
1624 crc = (crc >> 8) ^ (crcta[(c & 0xF0) >> 4] ^ crctb[c & 0x0F]);
1626 debug(F101,"chk3","",(unsigned int) (crc & 0xFFFF));
1627 return((unsigned int) (crc & 0xFFFF));
1629 #endif /* COMMENT */
1631 /* N X T P K T -- Next Packet */
1633 Get packet number of next packet to send and allocate a buffer for it.
1635 0 on success, with global pktnum set to the packet number;
1636 -1 on failure to allocate buffer (fatal);
1637 -2 if resulting packet number is outside the current window.
1640 nxtpkt() { /* Called by file sender */
1643 debug(F101,"nxtpkt pktnum","",pktnum);
1644 debug(F101,"nxtpkt winlo ","",winlo);
1645 n = (pktnum + 1) % 64; /* Increment packet number mod 64 */
1646 debug(F101,"nxtpkt n","",n);
1649 x = chkwin(n,winlo,wslots); /* Don't exceed window boundary */
1650 debug(F101,"nxtpkt chkwin","",x);
1653 j = getsbuf(n); /* Get a buffer for packet n */
1655 debug(F101,"nxtpkt getsbuf failure","",j);
1659 #endif /* STREAMING */
1664 /* Functions for sending ACKs and NAKs */
1666 /* Note, we should only ACK the packet at window-low (winlo) */
1667 /* However, if an old packet arrives again (e.g. because the ACK we sent */
1668 /* earlier was lost), we ACK it again. */
1671 ack() { /* Acknowledge the current packet. */
1672 return(ackns(winlo,(CHAR *)""));
1677 fastack() { /* Acknowledge packet n */
1681 k = rseqtbl[n]; /* First find received packet n. */
1682 debug(F101,"STREAMING fastack k","",k);
1683 freesbuf(n); /* Free current send-buffer, if any */
1684 if ((j = getsbuf(n)) < 0) {
1685 /* This can happen if we have to re-ACK an old packet that has */
1686 /* already left the window. It does no harm. */
1687 debug(F101,"STREAMING fastack can't getsbuf","",n);
1690 x = spack('Y',n,0,(CHAR *)""); /* Now send it (but not really) */
1692 if (x < 0) return(x);
1693 debug(F101,"STREAMING fastack x","",x);
1695 freerbuf(k); /* don't need it any more */
1697 freesbuf(j); /* and don't need to keep ACK either */
1698 winlo = (winlo + 1) % 64;
1701 #endif /* STREAMING */
1704 ackns(n,s) int n; CHAR *s; { /* Acknowledge packet n */
1706 debug(F111,"ackns",s,n);
1708 k = rseqtbl[n]; /* First find received packet n. */
1709 debug(F101,"ackns k","",k);
1710 freesbuf(n); /* Free current send-buffer, if any */
1711 if ((j = getsbuf(n)) < 0) {
1712 /* This can happen if we have to re-ACK an old packet that has */
1713 /* already left the window. It does no harm. */
1714 debug(F101,"ackns can't getsbuf","",n);
1716 x = spack('Y',n,(int)strlen((char *)s),s); /* Now send it. */
1717 if (x < 0) return(x);
1718 debug(F101,"ackns winlo","",winlo);
1719 debug(F101,"ackns n","",n);
1720 if (n == winlo) { /* If we're acking winlo */
1722 freerbuf(k); /* don't need it any more */
1724 freesbuf(j); /* and don't need to keep ACK either */
1725 winlo = (winlo + 1) % 64;
1731 ackn(n) int n; { /* Send ACK for packet number n */
1732 return(ackns(n,(CHAR *)""));
1736 ack1(s) CHAR *s; { /* Send an ACK with data. */
1737 if (!s) s = (CHAR *)"";
1738 debug(F110,"ack1",(char *)s,0);
1739 return(ackns(winlo,s));
1742 /* N A C K -- Send a Negative ACKnowledgment. */
1744 Call with the packet number, n, to be NAK'd.
1745 Returns -1 if that packet has been NAK'd too many times, otherwise 0.
1746 Btw, it is not right to return 0 under error conditions. This is
1747 done because the -1 code is used for cancelling the file transfer.
1748 More work is needed here.
1754 if (n < 0 || n > 63) {
1755 debug(F101,"nack bad pkt num","",n);
1757 } else debug(F101,"nack","",n);
1758 if ((i = sseqtbl[n]) < 0) { /* If necessary */
1759 if (getsbuf(n) < 0) { /* get a buffer for this NAK */
1760 debug(F101,"nack can't getsbuf","",n);
1762 } else i = sseqtbl[n]; /* New slot number */
1764 if (maxtry > 0 && s_pkt[i].pk_rtr++ > maxtry) /* How many? */
1765 return(-1); /* Too many... */
1767 /* Note, don't free this buffer. Eventually an ACK will come, and that */
1768 /* will set it free. If not, well, it's back to ground zero anyway... */
1770 x = spack('N',n,0,(CHAR *) ""); /* NAKs never have data. */
1774 #ifndef NEWDPL /* This routine no longer used */
1776 * (PWP) recalculate the optimal packet length in the face of errors.
1777 * This is a modified version of the algorithm by John Chandler in Kermit/370,
1778 * see "Dynamic Packet Size Control", Kermit News, V2 #1, June 1988.
1780 * This implementation minimizes the total overhead equation, which is
1782 * Total chars = file_chars + (header_len * num_packs)
1783 * + (errors * (header_len + packet_len))
1785 * Differentiate with respect to number of chars, solve for packet_len, get:
1787 * packet_len = sqrt (file_chars * header_len / errors)
1791 (FDC) New super-simple algorithm. If there was an error in the most recent
1792 packet exchange, cut the send-packet size in half, down to a minimum of 20.
1793 If there was no error, increase the size by 5/4, up to the maximum negotiated
1794 length. Seems to be much more responsive than previous algorithm, which took
1795 forever to recover the original packet length, and it also went crazy under
1798 Here's another idea for packet length resizing that keeps a history of the
1799 last n packets. Push a 1 into the left end of an n-bit shift register if the
1800 current packet is good, otherwise push a zero. The current n-bit value, w, of
1801 this register is a weighted sum of the noise hits for the last n packets, with
1802 the most recent weighing the most. The current packet length is some function
1803 of w and the negotiated packet length, like:
1805 (2^n - w) / (2^n) * (negotiated length)
1807 If the present resizing method causes problems, think about this one a little
1816 if (numerrs == 0) return; /* bounds check just in case */
1818 /* overhead on a data packet is npad+5+bctr, plus 3 if extended packet */
1819 /* an ACK is 5+bctr */
1821 /* first set x = per packet overhead */
1822 if (wslots > 1) /* Sliding windows */
1823 x = (long) (npad+5+bctr); /* packet only, don't count ack */
1824 else /* Stop-n-wait */
1825 x = (long) (npad+5+3+bctr+5+bctr); /* count packet and ack. */
1827 /* then set x = packet length ** 2 */
1828 x = x * ( ffc / (CK_OFF_T) numerrs); /* careful of overflow */
1830 /* calculate the long integer sqrt(x) quickly */
1835 q = (q + x/q) >> 1; /* should converge in about 4 steps */
1836 if ((q > 94) && (q < 130)) /* break-even point for long packets */
1838 if (q > spmax) q = spmax; /* maximum bounds */
1839 if (q < 10) q = 10; /* minimum bounds */
1840 spsiz = q; /* set new send packet size */
1841 debug(F101,"rcalcpsiz","",q);
1844 debug(F101,"rcalcpsiz numerrs","",numerrs);
1845 debug(F101,"rcalcpsiz spsiz","",spsiz);
1853 spsiz = (spsiz / 4) * 5;
1854 if (spsiz < 20) spsiz = 20;
1855 if (spsiz > spmax) spsiz = spmax;
1856 debug(F101,"rcalcpsiz new spsiz","",spsiz);
1858 #endif /* COMMENT */
1862 /* R E S E N D -- Retransmit packet n. */
1865 Returns 0 or positive on success (the number of retries for packet n).
1866 On failure, returns a negative number, and an error message is placed
1870 resend(n) int n; { /* Send packet n again. */
1873 CKFLOAT t1 = 0.0, t2 = 0.0;
1874 #endif /* GFTIMER */
1876 debug(F101,"resend seq","",n);
1878 k = chkwin(n,winlo,wslots); /* See if packet in current window */
1879 j = -1; /* Assume it's lost */
1880 if (k == 0) j = sseqtbl[n]; /* See if we still have a copy of it */
1881 if (k != 0 || j < 0) { /* If not.... */
1882 if (nakstate && k == 1) {
1884 Packet n is in the previous window and we are the file receiver.
1885 We already sent the ACK and deallocated its buffer so we can't just
1886 retransmit the ACK. Rather than give up, we try some tricks...
1888 if (n == 0 && spackets < 63 && myinit[0]) { /* ACK to Send-Init */
1890 If the packet number is 0, and we're at the beginning of a protocol
1891 operation (spackets < 63), then we have to resend the ACK to an I or S
1892 packet, complete with parameters in the data field. So we take a chance and
1893 send a copy of the parameters in an ACK packet with block check type 1.
1894 (Or 3 if SET BLOCK 5.)
1896 if (bctf) { /* Force Type 3 on all packets? */
1897 x = spack('Y',0,(int)strlen((char *)myinit),(CHAR *)myinit);
1898 if (x < 0) return(x);
1899 logpkt('#',n,(CHAR *)"<reconstructed>",0); /* Log it */
1900 } else { /* Regular Kermit protocol */
1901 int bctlsav; /* Temporary storage */
1903 bctlsav = bctl; /* Save current block check length */
1904 bctusav = bctu; /* and type */
1905 bctu = bctl = 1; /* Set block check to 1 */
1906 x = spack('Y',0,(int)strlen((char *)myinit),(CHAR *)myinit);
1907 if (x < 0) return(x);
1908 logpkt('#',n,(CHAR *)"<reconstructed>",0); /* Log it */
1909 bctu = bctusav; /* Restore block check type */
1910 bctl = bctlsav; /* and length */
1912 } else { /* Not the first packet */
1914 It's not the first packet of the protocol operation. It's some other packet
1915 that we have already ACK'd and forgotten about. So we take a chance and
1916 send an empty ACK using the current block-check type. Usually this will
1917 work out OK (like when acking Data packets), and no great harm will be done
1918 if it was some other kind of packet (F, etc). If we are requesting an
1919 interruption of the file transfer, the flags are still set, so we'll catch
1920 up on the next packet.
1922 x = spack('Y',n,0,(CHAR *) "");
1923 if (x < 0) return(x);
1926 xxscreen(SCR_PT,'%',(long)pktnum,"Retransmission");
1930 Packet number is not in current or previous window. We seem to hit this
1931 code occasionally at the beginning of a transaction, for apparently no good
1932 reason. Let's just log it for debugging, send nothing, and try to proceed
1933 with the protocol rather than killing it.
1935 debug(F101,"resend PKT NOT IN WINDOW","",n);
1936 debug(F101,"resend k","",k);
1941 /* OK, it's in the window and it's not lost. */
1943 debug(F101,"resend pktinfo index","",k);
1945 if (maxtry > 0 && s_pkt[j].pk_rtr++ > maxtry) { /* Over retry limit */
1949 debug(F101,"resend retry","",s_pkt[j].pk_rtr); /* OK so far */
1950 dumpsbuf(); /* (debugging) */
1951 if (s_pkt[j].pk_typ == ' ') { /* Incompletely formed packet */
1952 if (nakstate) { /* (This shouldn't happen any more) */
1955 xxscreen(SCR_PT,'%',(long)pktnum,"(resend)");
1956 return(s_pkt[j].pk_rtr);
1957 } else { /* No packet to resend! */
1960 This happened (once) while sending a file with 2 window slots and typing
1961 X to the sender to cancel the file. But since we're cancelling anyway,
1962 there's no need to give a scary message.
1964 sprintf((char *)epktmsg,
1965 "resend logic error: NPS, n=%d, j=%d.",n,j);
1968 /* Just ignore it. */
1970 #endif /* COMMENT */
1975 if (deblog) t1 = gftimer();
1976 #endif /* GFTIMER */
1979 /* Everything ok, send the packet */
1982 srttbl[n] = gtimer(); /* Update the timer */
1983 #endif /* CK_TIMERS */
1984 x = ttol(s_pkt[j].pk_adr,s_pkt[j].pk_len);
1990 debug(F101,"resend ttol msec","",(long)((t2-t1)*1000.0));
1992 #endif /* GFTIMER */
1994 debug(F101,"resend ttol returns","",x);
1996 retrans++; /* Count a retransmission */
1997 xxscreen(SCR_PT,'%',(long)pktnum,"(resend)"); /* Tell user about resend */
1998 logpkt('S',n,s_pkt[j].pk_adr, s_pkt[j].pk_len); /* Log the resent packet */
1999 return(s_pkt[j].pk_rtr); /* Return the number of retries. */
2002 /* E R R P K T -- Send an Error Packet */
2005 errpkt(reason) CHAR *reason; { /* ...containing the reason given */
2006 extern int rtimo, state, justone;
2008 czseen = 1; /* Also cancels batch */
2009 state = 0; /* Reset protocol state */
2010 debug(F110,"errpkt",reason,0);
2011 tlog(F110,"Protocol Error:",(char *)reason,0L);
2012 xxscreen(SCR_EM,0,0L,reason);
2014 x = spack('E',pktnum,size,data);
2015 ckstrncpy((char *)epktmsg,(char *)reason,PKTMSGLEN);
2016 y = quiet; quiet = 1; epktsent = 1; /* Close files silently. */
2020 I just sent an E-packet. I'm in local mode, I was receiving a file,
2021 I'm not a server, and sliding windows are in use. Therefore, there are
2022 likely to be a bunch of packets already "in the pipe" on their way to me
2023 by the time the remote sender gets the E-packet. So the next time I
2024 CONNECT or try to start another protocol operation, I am likely to become
2025 terribly confused by torrents of incoming material. To prevent this,
2026 the following code soaks up packets from the connection until there is an
2027 error or timeout, without wasting too much time waiting.
2029 Exactly the same problem occurs when I am in remote mode or if I am
2030 in server mode with the justone flag set. In remote mode not only
2031 does the packet data potentially get echo'd back to the sender which
2032 is confusing to the user in CONNECT mode, but it also may result in the
2033 host performing bizarre actions such as suspending the process if ^Z is
2036 Furthermore, thousands of packets bytes in the data stream prevent the
2037 client from being able to process Telnet Kermit Option negotiations
2041 /* Because streaming sets the timeout to 0... */
2043 timint = rcvtimo = rtimo;
2046 #endif /* STREAMING */
2047 if (what & W_RECV &&
2048 (!server || (server && justone)) &&
2052 #endif /* STREAMING */
2055 CKFLOAT oldsec, sec = (CKFLOAT) 0.0;
2057 int oldsec, sec = 0;
2058 #endif /* GFTIMER */
2059 debug(F101,"errpkt draining","",wslots);
2060 xxscreen(SCR_ST,ST_MSG,0l,"Draining incoming packets, wait...");
2061 while (x > -1) { /* Don't bother if no connection */
2065 if (oldsec != (CKFLOAT) 0.0)
2066 timint = rcvtimo = (int) (sec - oldsec + 0.5);
2070 timint = rcvtimo = sec - oldsec + 1;
2071 #endif /* GFTIMER */
2073 timint = rcvtimo = 1;
2074 msleep(50); /* Allow a bit of slop */
2075 x = rpack(); /* Read a packet */
2076 if (x == 'T' || x == 'z') /* Timed out means we're done */
2078 xxscreen(SCR_PT,x,rsn,""); /* Let user know */
2080 xxscreen(SCR_ST,ST_MSG,0l,"Drain complete.");
2082 if ((x = (what & W_KERMIT)))
2083 xitsta |= x; /* Remember what failed. */
2088 /* scmd() -- Send a packet of the given type */
2092 scmd(char t, CHAR *dat)
2094 scmd(t,dat) char t; CHAR *dat;
2095 #endif /* CK_ANSIC */
2098 extern char * srimsg;
2099 debug(F000,"scmd",dat,t);
2100 if (encstr(dat) < 0) { /* Encode the command string */
2101 srimsg = "String too long";
2104 x = spack(t,pktnum,size,data);
2105 debug(F101,"scmd spack","",x);
2109 /* Compose and Send GET packet */
2111 struct opktparm { /* O-Packet item list */
2113 struct opktparm * opktnext;
2116 struct opktparm * opkthead = NULL; /* Linked list of O-packet fields */
2117 int opktcnt = 0; /* O-Packet counter */
2118 char * srimsg = NULL; /* GET-Packet error message */
2120 /* S O P K T -- Send O-Packet */
2122 Sends one O-Packet each time called, using first-fit method of filling
2123 the packet from linked list of parameters pointed to by opkthead.
2124 To be called repeatedly until list is empty or there is an error.
2127 0 on success and no more fields left to send.
2128 1 on success but with more fields left to be sent.
2133 int n = 0; /* Field number in this packet */
2134 int rc = 0; /* Return code */
2135 int len = 0; /* Data field length */
2137 struct opktparm * o = NULL;
2138 struct opktparm * t = NULL;
2139 struct opktparm * prev = NULL;
2140 CHAR * dsave = data;
2141 int x, ssave = spsiz;
2143 srimsg = NULL; /* Error message */
2144 o = opkthead; /* Point to head of list */
2145 if (!o) { /* Oops, no list... */
2146 srimsg = "GET Packet Internal Error 1";
2147 debug(F100,"sopkt NULL list","",0);
2150 while (o) { /* Go thru linked list... */
2151 c = *(o->opktitem); /* Parameter code */
2152 debug(F000,"sopkt",o->opktitem,c);
2153 x = encstr((CHAR *)o->opktitem);
2154 debug(F111,"sopkt encstr",dsave,x);
2155 if (x < 0) { /* Encode this item */
2156 if (n == 0) { /* Failure, first field in packet */
2157 debug(F100,"sopkt overflow","",0);
2158 spsiz = ssave; /* Restore these */
2160 o = opkthead; /* Free linked list */
2162 if (o->opktitem) free(o->opktitem);
2168 srimsg = "GET Packet Too Long for Server";
2169 return(-1); /* Fail */
2170 } else { /* Not first field in packet */
2171 debug(F110,"sopkt leftover",o->opktitem,0);
2172 prev = o; /* Make this one the new previous */
2173 o = o->opktnext; /* Get next */
2174 c = NUL; /* So we know we're not done */
2175 *data = NUL; /* Erase any partial encoding */
2176 continue; /* We can try this one again later */
2179 n++; /* Encoding was successful */
2180 debug(F111,"sopkt field",data,x);
2181 len += x; /* Total data field length */
2182 data += x; /* Set up for next field... */
2184 free(o->opktitem); /* Free item just encoded */
2185 if (o == opkthead) { /* If head */
2186 opkthead = o->opktnext; /* Move head to next */
2187 free((char *)o); /* Free this list node */
2189 } else { /* If not head */
2190 o = o->opktnext; /* Get next */
2191 prev->opktnext = o; /* Link previous to next */
2193 if (c == '@') /* Loop exit */
2195 if (!o && !opkthead) { /* Set up End Of Parameters Field */
2196 o = (struct opktparm *)malloc(sizeof(struct opktparm));
2199 if (!(o->opktitem = (CHAR *)malloc(3))) {
2201 srimsg = "GET Packet Internal Error 8";
2204 ckstrncpy((char *)(o->opktitem), "@ ", 3);
2205 debug(F111,"sopkt o->opktitem",o->opktitem,
2206 strlen((char *)(o->opktitem)));
2211 data = dsave; /* Restore globals */
2213 debug(F110,"sopkt data",data,0);
2214 debug(F101,"sopkt opktcnt","",opktcnt);
2215 if (opktcnt++ > 0) {
2216 if (nxtpkt() < 0) { /* Get next packet number and buffer */
2217 srimsg = "GET Packet Internal Error 9";
2221 debug(F101,"sopkt pktnum","",pktnum);
2222 rc = spack((char)'O',pktnum,len,data); /* Send O-Packet */
2223 debug(F101,"sopkt spack","",rc);
2224 if (rc < 0) /* Failed */
2225 srimsg = "Send Packet Failure"; /* Set message */
2226 else /* Succeeded */
2227 rc = (c == '@') ? 0 : 1; /* 1 = come back for more, 0 = done */
2228 debug(F101,"sopkt rc","",rc);
2232 /* S R I N I T -- Send GET packet */
2234 Sends the appropriate GET-Class packet.
2237 0 if packet sent successfully and we can move on to the next state
2238 1 if an O-packet was sent OK but more O packets still need to be sent.
2241 srinit(reget, retrieve, opkt) int reget, retrieve, opkt; {
2242 int x = 0, left = 0;
2243 extern int oopts, omode;
2246 extern int recursive;
2247 debug(F101,"srinit recursive","",recursive);
2248 #endif /* RECURSIVE */
2249 debug(F101,"srinit reget","",reget);
2250 debug(F101,"srinit retrieve","",retrieve);
2251 debug(F101,"srinit opkt","",opkt);
2252 debug(F101,"srinit oopts","",oopts);
2253 debug(F101,"srinit omode","",omode);
2254 debug(F110,"srinit cmarg",cmarg,0);
2258 if (!cmarg) cmarg = "";
2260 srimsg = "GET with no filename";
2261 debug(F100,"srinit null cmarg","",0);
2264 if (opkt) { /* Extended GET is totally different */
2266 struct opktparm * o = NULL;
2267 struct opktparm * prev = NULL;
2271 /* Build O-Packet fields and send (perhaps first) O-Packet */
2273 if (oopts > -1) { /* Write Option flags */
2274 o = (struct opktparm *)malloc(sizeof(struct opktparm));
2276 srimsg = "GET Packet Internal Error 2";
2277 debug(F100,"srinit malloc fail O1","",0);
2280 sprintf(buf,"Ox%d",oopts); /* safe */
2281 x = (int) strlen(buf+2);
2283 o->opktitem = (CHAR *)malloc(x + 3);
2285 srimsg = "GET Packet Internal Error 3";
2286 debug(F100,"srinit malloc fail O2","",0);
2289 ckstrncpy((char *)(o->opktitem),buf,x+3);
2295 if (omode > -1) { /* If Xfer Mode specified, write it */
2296 o = (struct opktparm *)malloc(sizeof(struct opktparm));
2298 srimsg = "GET Packet Internal Error 4";
2299 debug(F100,"srinit malloc fail M1","",0);
2302 sprintf(buf,"Mx%d",omode); /* safe */
2303 x = (int) strlen(buf+2);
2305 o->opktitem = (CHAR *)malloc(x + 3);
2307 srimsg = "GET Packet Internal Error 5";
2308 debug(F100,"srinit malloc fail O2","",0);
2311 ckstrncpy((char *)(o->opktitem),buf,x+3);
2320 /* Same deal for oname and opath eventually but not needed now... */
2322 x = strlen(cmarg); /* Now do filename */
2323 if (x > spsiz - 4) {
2324 srimsg = "GET Packet Too Long for Server";
2327 o = (struct opktparm *)malloc(sizeof(struct opktparm));
2329 srimsg = "GET Packet Internal Error 6";
2330 debug(F100,"srinit malloc fail F1","",0);
2334 o->opktitem = (CHAR *)malloc(left + 1);
2336 srimsg = "GET Packet Internal Error 7";
2337 debug(F100,"srinit malloc fail F2","",0);
2343 if (x > 94) { /* Too long for normal length */
2344 *p++ = SYN; /* Escape length with Ctrl-V */
2345 *p++ = tochar(x / 95);
2346 *p++ = tochar(x % 95);
2348 } else { /* Normal encoding for 94 or less */
2352 ckstrncpy((char *)p,cmarg,left); /* Copy the filename */
2360 /* End of Parameters */
2362 prev->opktnext = NULL; /* End of list. */
2366 /* Not Extended GET */
2368 if (encstr((CHAR *)cmarg) < 0) { /* Encode the filename. */
2369 srimsg = "GET Packet Too Long for Server";
2372 if (retrieve) { /* Send the packet. */
2375 x = spack((char)'W',pktnum,size,data); /* GET /DELETE /RECURSIVE */
2377 #endif /* RECURSIVE */
2378 x = spack((char)'H',pktnum,size,data); /* GET /DELETE */
2382 x = spack((char)'V',pktnum,size,data); /* GET /RECURSIVE */
2383 #endif /* RECURSIVE */
2385 x = spack((char)(reget ? 'J' : 'R'),pktnum,size,data); /* GET */
2387 srimsg = "Send Packet Failure";
2388 return(x < 0 ? x : 0);
2392 /* K S T A R T -- Checks for a Kermit packet while in terminal mode. */
2394 /* (or command mode...) */
2402 #endif /* CK_ANSIC */
2404 static CHAR * p = NULL;
2407 static CHAR * pk = NULL;
2409 ch &= 0177; /* Strip 8th bit */
2411 /* Because we're in cooked mode at the command prompt... */
2414 debug(F110,"kstart","ch == LF",0);
2415 if ((what == W_COMMAND || what == W_INIT || what == W_NOTHING)) {
2418 debug(F110,"kstart","ch = CR",0);
2424 if (adl_kmode == ADL_STR) {
2434 debug(F100, "kstart Kermit Start String","",0);
2435 return(PROTO_K + 1);
2442 if (ch == stchr) { /* Start of packet */
2446 debug(F101,"kstart SOP","",ch);
2447 } else if (ch == eol) { /* End of packet */
2450 debug(F101,"kstart EOL","",ch);
2452 if (p - ksbuf < 94 ) {
2456 rc = chkspkt((char *)ksbuf);
2457 debug(F111,"kstart EOP chkspkt", ksbuf, rc);
2460 if (rc == 2) rc = -1;
2461 debug(F111,"kstart ksbuf",ksbuf,rc);
2464 debug(F110,"kstart","p - ksbuf >= 94",0);
2472 if (p - ksbuf < 94) {
2476 debug(F110,"kstart","p - ksbuf >= 94",0);
2484 /* Z S T A R T -- Checks for a ZMODEM packet while in terminal mode. */
2491 #endif /* CK_ANSIC */
2493 static CHAR * matchstr = (CHAR *) "\030B00";
2494 /* "rz\r**\030B00000000000000\r\033J\021"; */
2495 static CHAR * p = NULL;
2496 extern int inserver;
2505 p = adl_zmode == ADL_PACK ? matchstr : adl_zstr;
2514 if (adl_zmode == ADL_PACK) {
2516 debug(F100, "zstart Zmodem SOP","",0);
2519 debug(F100, "zstart Zmodem Start String","",0);
2523 debug(F100, "zstart Zmodem SOP","",0);
2525 return(PROTO_Z + 1);
2529 p = adl_zmode == ADL_PACK ? matchstr : adl_zstr;
2540 /* A U T O D O W N */
2547 autodown(ch) int ch;
2548 #endif /* CK_ANSIC */
2551 /* The Kermit and Zmodem Auto-download calls go here */
2553 extern int justone; /* From protocol module */
2554 extern int debses, protocol, apcactive, autodl, inautodl;
2556 extern char *apcbuf;
2558 extern char apcbuf[];
2559 #endif /* DCMDBUF */
2561 extern int apclength, term_io;
2565 if ((autodl || inautodl
2567 || TELOPT_SB(TELOPT_KERMIT).kermit.me_start
2568 #endif /* IKS_OPTION */
2575 #endif /* XYZ_INTERNAL */
2576 if (p_avail && zstart((CHAR) ch)) {
2577 debug(F100, "Zmodem download","",0);
2580 apc_command(APC_LOCAL,"receive /protocol:zmodem");
2583 ckstrncpy(apcbuf,"receive /protocol:zmodem",APCBUFLEN);
2584 apcactive = APC_LOCAL;
2591 k = kstart((CHAR) ch);
2595 #else /* NOSERVER */
2597 #endif /* NOSERVER */
2598 ) { /* We saw a valid S or I packet */
2599 if (k < 0) { /* Stuff RECEIVE into APC buffer */
2605 "set proto kermit, server, set protocol g",
2611 "set proto kermit,server,set proto xmodem",
2617 "set proto kermit,server,set proto xmodem-crc",
2623 "set proto kermit,server, set protocol y",
2629 "set proto kermit,server,set proto zmodem",
2635 ckstrncpy(apcbuf,"server",APCBUFLEN);
2640 ckstrncpy(apcbuf,"receive /protocol:kermit",APCBUFLEN);
2644 apc_command(APC_LOCAL,apcbuf);
2647 ckstrncpy(apcbuf,"receive /protocol:zmodem",APCBUFLEN);
2648 apcactive = APC_LOCAL;
2657 /* C H K S P K T -- Check if buf contains a valid S or I packet */
2660 chkspkt(packet) char *packet; {
2668 char tmpbuf[100]; /* Longest S/I packet is about 30 */
2670 if (!packet) return(0);
2671 buflen = ckstrncpy(tmpbuf,packet,100); /* Make a pokeable copy */
2672 if (buflen < 5) return(0); /* Too short */
2673 if (buflen > 100) return(0); /* Too long to be an S or I packet */
2674 s = buf = tmpbuf; /* Point to beginning of copy */
2676 if (*s++ != stchr) return(0); /* SOH */
2677 len = xunchar(*s++); /* Length */
2678 if (len < 0) return(0);
2679 if (*s++ != SP) return(0); /* Sequence number */
2680 type = *s++; /* Type */
2681 if (type != 'S' && type != 'I')
2683 if (buflen < len + 2) return(0);
2684 s += (len - 3); /* Position of checksum */
2685 chk = (CHAR) (*s); /* Checksum */
2686 *s = NUL; /* Temporarily null-terminate data field */
2687 if (xunchar(chk) != chk1((CHAR *)(buf+1),buflen-2)) { /* Check it */
2689 In C-Kermit 9.0 and later, an S or I packet can have a
2690 Type 3 Block check ("help set block-check" for details).
2692 unsigned crc; /* Failed... Try Type 3 block check */
2693 *s = chk; /* Replace last byte */
2694 s -= 2; /* Back up two bytes */
2695 crc = (xunchar(s[0]) << 12) /* Convert 3 bytes to numeric CRC */
2696 | (xunchar(s[1]) << 6)
2698 chk = (CHAR)(*s); /* Copy 1st byte of 3-byte CRC */
2699 *s = NUL; /* Null-terminate data field */
2700 if (crc != chk3((CHAR *)(buf+1),strlen(buf+1)))
2703 return(type == 'S' ? 1 : 2);
2705 #endif /* CK_AUTODL */
2707 /* R P A C K -- Read a Packet */
2710 rpack reads a packet and returns the packet type, or else Q if the
2711 packet was invalid, or T if a timeout occurred. Upon successful return,
2712 sets the values of global rsn (received sequence number), rln (received
2713 data length), and rdatap (pointer to null-terminated data field), and
2714 returns the packet type. NOTE: This is an inner-loop function so must be
2715 efficient. Protect function calls by if-tests where possible, e.g.
2716 "if (pktlog) logpkt(...);".
2720 register int i, j, x, lp; /* Local variables */
2723 #endif /* CKTUNING */
2724 int k, type, chklen;
2726 CHAR pbc[5]; /* Packet block check */
2727 CHAR *sohp; /* Pointer to SOH */
2728 CHAR e; /* Packet end character */
2731 CKFLOAT t1 = 0.0, t2 = 0.0;
2732 #endif /* GFTIMER */
2734 debug(F101,"rpack pktnum","",pktnum);
2737 if (chkint() < 0) /* Check for console interrupts. */
2739 #endif /* OLDCHKINT */
2741 k = getrbuf(); /* Get a new packet input buffer. */
2742 debug(F101,"rpack getrbuf","",k);
2743 if (k < 0) { /* Return like this if none free. */
2746 recpkt = r_pkt[k].bf_adr;
2747 *recpkt = '\0'; /* Clear receive buffer. */
2748 sohp = recpkt; /* Initialize pointers to it. */
2750 rsn = rln = -1; /* In case of failure. */
2751 e = (turn) ? turnch : eol; /* Use any handshake char for eol */
2753 /* Try to get a "line". */
2756 debug(F110,"rpack ksbuf",ksbuf,0);
2757 if (ksbuf[0]) { /* Kermit packet already */
2758 int x; /* collected for us in CONNECT mode */
2759 CHAR *s1 = recpkt, *s2 = ksbuf;
2761 while (*s2) { /* Copy and get length */
2762 *s1++ = *s2++; /* No point optimizing this since */
2763 j++; /* it's never more than ~20 chars */
2767 x = parchk(recpkt, stchr, j); /* Check parity */
2768 debug(F000,"autodownload parity","",parity);
2769 debug(F000,"autodownload parchk","",x);
2770 if (x > 0 && parity != x) {
2774 #endif /* PARSENSE */
2775 ksbuf[0] = NUL; /* Don't do this next time! */
2777 } else { /* Normally go read a packet */
2778 #endif /* CK_AUTODL */
2782 debug(F101,"rpack timint","",timint);
2783 debug(F101,"rpack rcvtimo","",rcvtimo);
2785 debug(F101,"rpack streaming","",streaming);
2786 #endif /* STREAMING */
2788 /* Measure how long it takes to read a packet */
2790 #endif /* GFTIMER */
2794 /* JUST IN CASE (otherwise this could clobber streaming) */
2799 #endif /* STREAMING */
2800 ) && (rcvtimo != 0)) {
2801 debug(F101,"rpack timint 0 || streaming but rcvtimo","",rcvtimo);
2808 So far the final turn argument is only for ck[uvdl]tio.c. Should be added
2809 to the others too. (turn == handshake character.)
2811 j = ttinl(recpkt,r_pkt[k].bf_len - 1,rcvtimo,e,stchr,turn);
2814 j = ttinl(recpkt,r_pkt[k].bf_len - 1,rcvtimo,e,stchr,turn);
2817 j = ttinl(recpkt,r_pkt[k].bf_len - 1,rcvtimo,e,stchr,turn);
2820 j = ttinl(recpkt,r_pkt[k].bf_len - 1,rcvtimo,e,stchr,turn);
2823 j = ttinl(recpkt,r_pkt[k].bf_len - 1,rcvtimo,e,stchr,turn);
2826 j = ttinl(recpkt,r_pkt[k].bf_len - 1,rcvtimo,e,stchr,turn);
2828 j = ttinl(recpkt,r_pkt[k].bf_len - 1,rcvtimo,e,stchr);
2831 #endif /* STRATUS */
2832 #endif /* datageneral */
2835 if (parity != 0 && parity != 's' && ttprty != 0) {
2836 if (parity != ttprty) autopar = 1;
2839 #else /* !PARSENSE */
2840 j = ttinl(recpkt,r_pkt[k].bf_len - 1,rcvtimo,e);
2841 #endif /* PARSENSE */
2845 debug(F101,"rpack ttinl len","",j);
2848 debug(F101,"rpack ttinl msec","",(long)((t2-t1)*1000.0));
2849 #endif /* GFTIMER */
2854 if (streaming && sndtyp == 'D' && j == 0)
2856 #endif /* STREAMING */
2859 /* -1 == timeout, -2 == ^C, -3 == connection lost or fatal i/o */
2860 debug(F101,"rpack: ttinl fails","",j); /* Otherwise, */
2861 freerbuf(k); /* Free this buffer */
2862 if (j < -1) { /* Bail out if ^C^C typed. */
2865 debug(F101,"rpack ^C server","",server);
2866 debug(F101,"rpack ^C en_fin","",en_fin);
2867 } else if (j == -3) {
2869 debug(F101,"rpack fatalio","",en_fin);
2873 if (nakstate) /* j == -1 is a read timeout */
2874 xxscreen(SCR_PT,'T',(long)winlo,"");
2876 xxscreen(SCR_PT,'T',(long)pktnum,"");
2877 logpkt('r',-1,(CHAR *)"<timeout>",0);
2878 if (flow == 1) ttoc(XON); /* In case of Xoff blockage. */
2883 #endif /* CK_AUTODL */
2886 tlci += j; /* All OK, Count the characters. */
2889 /* Find start of packet */
2892 for (i = 0; (recpkt[i] != stchr) && (i < j); i++)
2893 sohp++; /* Find mark */
2894 if (i++ >= j) { /* Didn't find it. */
2895 logpkt('r',-1,"<timeout>",0);
2900 i = 1; /* ttinl does this for us */
2901 #endif /* PARSENSE */
2903 rpackets++; /* Count received packet. */
2904 lp = i; /* Remember LEN position. */
2905 if ((j = xunchar(recpkt[i++])) == 0) { /* Get packet length. */
2906 if ((j = lp+5) > MAXRP) { /* Long packet */
2907 return('Q'); /* Too long */
2911 /* Save some function-call and loop overhead... */
2913 /* ttinl() already removed parity */
2915 #endif /* COMMENT */
2916 chk = (unsigned) ((unsigned) recpkt[i-1] +
2917 (unsigned) recpkt[i] +
2918 (unsigned) recpkt[i+1] +
2919 (unsigned) recpkt[i+2] +
2920 (unsigned) recpkt[i+3]
2924 chk = (unsigned) ((unsigned) (recpkt[i-1] & 077) +
2925 (unsigned) (recpkt[i] & 077) +
2926 (unsigned) (recpkt[i+1] & 077) +
2927 (unsigned) (recpkt[i+2] & 077) +
2928 (unsigned) (recpkt[i+3] & 077)
2930 #endif /* COMMENT */
2931 if (xunchar(recpkt[j]) != ((((chk & 0300) >> 6) + chk) & 077))
2933 x = recpkt[j]; /* Header checksum. */
2934 recpkt[j] = '\0'; /* Calculate & compare. */
2935 if (xunchar(x) != chk1(recpkt+lp,5))
2936 #endif /* CKTUNING */
2939 logpkt('r',-1,(CHAR *)"<crunched:hdr>",0);
2940 xxscreen(SCR_PT,'%',(long)pktnum,"Bad packet header");
2944 recpkt[j] = x; /* Checksum ok, put it back. */
2945 #endif /* CKTUNING */
2946 rln = xunchar(recpkt[j-2]) * 95 + xunchar(recpkt[j-1]) - bctl;
2947 j = 3; /* Data offset. */
2949 debug(F101,"rpack packet length less than 3","",j);
2951 logpkt('r',-1,(CHAR *)"<crunched:len>",0);
2952 xxscreen(SCR_PT,'%',(long)pktnum,"Bad packet length");
2955 rln = j - bctl - 2; /* Regular packet */
2956 j = 0; /* No extended header */
2958 rsn = xunchar(recpkt[i++]); /* Sequence number */
2959 if (pktlog) /* Save a function call! */
2960 logpkt('r',rsn,sohp,rln+bctl+j+4);
2961 if (rsn < 0 || rsn > 63) {
2962 debug(F101,"rpack bad sequence number","",rsn);
2965 logpkt('r',rsn,(CHAR *)"<crunched:seq>",0);
2966 xxscreen(SCR_PT,'%',(long)pktnum,"Bad sequence number");
2970 If this packet has the same type as the packet just sent, assume it is
2971 an echo and ignore it. Don't even bother with the block check calculation:
2972 even if the packet is corrupted, we don't want to NAK an echoed packet.
2973 Nor must we NAK an ACK or NAK.
2975 type = recpkt[i++]; /* Get packet's TYPE field */
2976 if (type == sndtyp || (nakstate && (type == 'N' /* || type == 'Y' */ ))) {
2977 debug(F000,"rpack echo","",type); /* If it's an echo */
2978 freerbuf(k); /* Free this buffer */
2979 logpkt('#',rsn,(CHAR *)"<echo:ignored>",0);
2980 return('e'); /* Return special (lowercase) code */
2983 Separate the data from the block check, accounting for the case where
2984 a packet was retransmitted after the block check switched. The "Type 3
2985 Forced" business is new to C-Kermit 9.0.
2987 if (bctf) { /* Type 3 forced on all packets */
2989 } else if ((type == 'I' || type == 'S')) { /* Otherwise... */
2990 if (recpkt[11] == '5') { /* Sender is forcing Type 3 */
2991 bctf = 1; /* So we will too */
2993 debug(F100,"RECOGNIZE BLOCK CHECK TYPE 5","",0);
2994 } else { /* Normal case */
2995 /* I & S packets always have type 1 */
2997 rln = rln + bctl - 1;
2999 } else if (type == 'N') { /* A NAK packet never has data */
3000 chklen = xunchar(recpkt[lp]) - 2;
3001 if (chklen < 1 || chklen > 3) { /* JHD 13 Apr 2010 */
3002 debug(F101,"rpack bad nak chklen","",chklen);
3004 logpkt('r',-1,(CHAR *)"<crunched:chklen>",0);
3005 xxscreen(SCR_PT,'%',(long)pktnum,"(bad nak)");
3008 rln = rln + bctl - chklen;
3009 } else chklen = bctl;
3011 if (deblog) { /* Save 2 function calls */
3012 debug(F101,"rpack bctl","",bctl);
3013 debug(F101,"rpack chklen","",chklen);
3016 i += j; /* Buffer index of DATA field */
3017 rdatap = recpkt+i; /* Pointer to DATA field */
3018 if ((j = rln + i) > r_pkt[k].bf_len) { /* Make sure it fits */
3019 debug(F101,"packet too long","",j);
3021 logpkt('r',rsn,(CHAR *)"<overflow>",0);
3024 for (x = 0; x < chklen; x++) /* Copy the block check */
3025 pbc[x] = recpkt[j+x]; /* 3 bytes at most. */
3026 pbc[x] = '\0'; /* Null-terminate block check string */
3027 recpkt[j] = '\0'; /* and the packet Data field. */
3029 if (chklen == 2 && bctu == 4) { /* Adjust for Blank-Free-2 */
3030 chklen = 4; /* (chklen is now a misnomer...) */
3031 debug(F100,"rpack block check B","",0);
3033 switch (chklen) { /* Check the block check */
3034 case 1: /* Type 1, 6-bit checksum */
3035 if (xunchar(*pbc) != chk1(recpkt+lp,j-lp)) {
3038 debug(F110,"checked chars",recpkt+lp,0);
3039 debug(F101,"block check (1)","",(int) xunchar(*pbc));
3040 debug(F101,"should be (1)","",chk1(recpkt+lp,j-lp));
3044 logpkt('r',-1,(CHAR *)"<crunched:chk1>",0);
3045 xxscreen(SCR_PT,'%',(long)pktnum,"Checksum error");
3049 case 2: /* Type 2, 12-bit checksum */
3050 x = xunchar(*pbc) << 6 | xunchar(pbc[1]);
3051 if (x != chk2(recpkt+lp,j-lp)) { /* No match */
3052 if (type == 'E') { /* Allow E packets to have type 1 */
3053 recpkt[j++] = pbc[0];
3055 if (xunchar(pbc[1]) == chk1(recpkt+lp,j-lp))
3062 debug(F110,"checked chars",recpkt+lp,0);
3063 debug(F101,"block check (2)","", x);
3064 debug(F101,"should be (2)","", (int) chk2(recpkt+lp,j-lp));
3068 logpkt('r',-1,(CHAR *)"<crunched:chk2>",0);
3069 xxscreen(SCR_PT,'%',(long)pktnum,"Checksum error");
3073 case 3: /* Type 3, 16-bit CRC */
3074 crc = (xunchar(pbc[0]) << 12)
3075 | (xunchar(pbc[1]) << 6)
3076 | (xunchar(pbc[2]));
3077 if (crc != chk3(recpkt+lp,j-lp)) {
3078 if (type == 'E') { /* Allow E packets to have type 1 */
3079 recpkt[j++] = pbc[0];
3080 recpkt[j++] = pbc[1];
3082 if (xunchar(pbc[2]) == chk1(recpkt+lp,j-lp))
3084 else { j -=2; recpkt[j] = '\0'; }
3088 debug(F110,"checked chars",recpkt+lp,0);
3089 debug(F101,"block check (3)","",crc);
3090 debug(F101,"should be (3)","",(int) chk3(recpkt+lp,j-lp));
3094 logpkt('r',-1,(CHAR *)"<crunched:chk3>",0);
3095 xxscreen(SCR_PT,'%',(long)pktnum,"CRC error");
3099 case 4: /* Type 4 = Type 2, no blanks. */
3100 x = (unsigned)((xunchar(*pbc) - 1) << 6) |
3101 (unsigned)(xunchar(pbc[1]) - 1);
3102 if (x != chk2(recpkt+lp,j-lp)) {
3103 if (type == 'E') { /* Allow E packets to have type 1 */
3104 recpkt[j++] = pbc[0];
3106 if (xunchar(pbc[1]) == chk1(recpkt+lp,j-lp))
3111 debug(F101,"bad type B block check","",x);
3113 logpkt('r',-1,(CHAR *)"<crunched:chkb>",0);
3114 xxscreen(SCR_PT,'%',(long)pktnum,"Checksum error");
3118 default: /* Shouldn't happen... */
3120 logpkt('r',-1,(CHAR *)"<crunched:chkx>",0);
3121 xxscreen(SCR_PT,'%',(long)pktnum,"(crunched)");
3124 debug(F101,"rpack block check OK","",rsn);
3126 /* Now we can believe the sequence number, and other fields. */
3127 /* Here we violate strict principles of layering, etc, and look at the */
3128 /* packet sequence number. If there's already a packet with the same */
3129 /* number in the window, we remove this one so that the window will not */
3132 if ((x = rseqtbl[rsn]) != -1) { /* Already a packet with this number */
3133 retrans++; /* Count it for statistics */
3134 debug(F101,"rpack got dup","",rsn);
3135 logpkt('r',rsn,(CHAR *)"<duplicate>",0);
3136 freerbuf(x); /* Free old buffer, keep new packet. */
3137 r_pkt[k].pk_rtr++; /* Count this as a retransmission. */
3140 /* New packet, not seen before, enter it into the receive window. */
3144 rrttbl[rsn] = gtimer(); /* Timestamp */
3145 #endif /* CK_TIMERS */
3147 rseqtbl[rsn] = k; /* Make back pointer */
3148 r_pkt[k].pk_seq = rsn; /* Record in packet info structure */
3149 r_pkt[k].pk_typ = type; /* Sequence, type,... */
3150 r_pkt[k].pk_adr = rdatap; /* pointer to data buffer */
3151 if (local) { /* Save a function call! */
3153 if (fdispla != XYFD_N) x = 1;
3154 if (fdispla == XYFD_B && (type == 'D' || sndtyp == 'D')) x = 0;
3155 if (x) /* Update screen */
3156 xxscreen(SCR_PT,(char)type,(long)rsn,(char *)sohp);
3158 return(type); /* Return packet type */
3161 /* L O G P K T -- Log packet number n, pointed to by s. */
3163 /* c = 's' (send) or 'r' (receive) */
3167 logpkt(char c,int n, CHAR *s, int len)
3169 logpkt(c,n,s,len) char c; int n; CHAR *s; int len;
3170 #endif /* CK_ANSIC */
3173 if (!s) s = (CHAR *)"";
3174 if (pktlog) if (chkfn(ZPFILE) > 0) {
3175 if (n < 0) /* Construct entry header */
3176 sprintf(plog,"%c-xx-%02d-",c,(gtimer()%60)); /* safe */
3178 sprintf(plog,"%c-%02d-%02d-",c,n,(gtimer()%60)); /* safe */
3179 if (zsoutx(ZPFILE,plog,(int)strlen(plog)) < 0) {
3184 len = strlen((char *)s);
3186 char * p; /* Make SOP printable */
3187 int x; /* so we can look at logs without */
3188 p = dbchr(*s); /* triggering autodownload. */
3189 x = strlen(dbchr(*s));
3190 if (*s < 32 || (*s > 127 && *s < 160)) {
3191 if (zsoutx(ZPFILE,p,x) < 0) {
3200 if (zsoutx(ZPFILE,(char *)s,len) < 0) {
3203 } else if (zsoutx(ZPFILE,
3219 #endif /* datageneral */
3228 /* T S T A T S -- Record statistics in transaction log */
3234 CKFLOAT xx; /* Elapsed time divisor */
3235 #endif /* GFTIMER */
3237 debug(F101,"tstats xfsecs","",xfsecs);
3238 debug(F101,"tstats filcnt","",filcnt);
3239 if (filcnt == 1) { /* Get timing for statistics */
3240 tsecs = xfsecs; /* Single file, we already have it */
3242 debug(F101,"tstats fpxfsecs","",(int)fpxfsecs);
3244 #endif /* GFTIMER */
3245 } else { /* Multiple files */
3246 tsecs = gtimer(); /* Get current time */
3248 fptsecs = gftimer();
3249 #endif /* GFTIMER */
3252 if (fptsecs <= GFMINTIME) /* Calculate CPS */
3253 fptsecs = (CKFLOAT) GFMINTIME;
3254 debug(F101,"tstats fptsecs","",(int)fptsecs);
3255 xx = (CKFLOAT) tfc / fptsecs;
3256 if (sizeof(long) <= 4) { /* doesn't account for 16-bit longs */
3257 if (xx > 2147483647.0)
3258 tfcps = 2147483647L; /* 31 bits */
3266 debug(F101,"tstats tsecs","",tsecs);
3267 tfcps = tfc / tsecs;
3268 #endif /* GFTIMER */
3270 ztime(&tp); /* Get time stamp */
3271 tlog(F100,"","",0L); /* Leave a blank line */
3272 tlog(F110,"Transaction complete",tp,0L); /* Record it */
3274 if (filcnt < 1) return; /* If no files, done. */
3276 /* If multiple files, record character totals for all files */
3279 tlog(F101," files transferred ","",filcnt - filrej);
3280 tlog(F101," total file characters ","",tfc);
3281 tlog(F101," communication line in ","",tlci);
3282 tlog(F101," communication line out ","",tlco);
3285 /* Record timing info for one or more files */
3288 if (filcnt - filrej == 1) {
3289 tlog(F101," elapsed time (seconds) ","",(long) fpxfsecs);
3290 tlog(F101," effective data rate ","",filcps);
3292 tlog(F101," elapsed time (seconds) ","",(long) fptsecs);
3293 tlog(F101," effective data rate ","",(long) xx);
3296 tlog(F101," elapsed time (seconds) ","",tsecs);
3298 tlog(F101," effective data rate ","",(tfc / tsecs));
3299 #endif /* GFTIMER */
3301 tlog(F100,"","",0L); /* Leave a blank line */
3304 /* F S T A T S -- Record file statistics in transaction log */
3310 fpxfsecs = gftimer() - fpfsecs;
3311 if (fpxfsecs <= GFMINTIME)
3312 fpxfsecs = (CKFLOAT) GFMINTIME;
3313 xx = (CKFLOAT) ffc / fpxfsecs;
3314 if (sizeof(long) <= 4) {
3315 if (xx > 2147483647.0)
3316 tfcps = 2147483647L; /* 31 bits */
3321 if (sizeof(int) >= 4)
3322 xfsecs = (int) fpxfsecs;
3323 else if (fpxfsecs < 32768.0)
3324 xfsecs = (int) fpxfsecs;
3328 xfsecs = gtimer() - fsecs;
3329 if (xfsecs < 1L) xfsecs = 1L;
3330 filcps = ffc / xfsecs;
3331 #endif /* GFTIMER */
3339 debug(F101,"fstats tfc","",tfc);
3340 debug(F101,"fstats what","",what);
3341 debug(F110,"fstats epktmsg",epktmsg,0);
3345 if (!discard && !cxseen && !czseen && what != W_NOTHING && !*epktmsg)
3346 tlog(F101," complete, size","",ffc);