Commit | Line | Data |
---|---|---|
cdd19d13 C |
1 | /* C K C F N 2 -- System-independent Kermit protocol support functions... */ |
2 | ||
3 | /* ...Part 2 (continued from ckcfns.c) */ | |
4 | ||
5 | /* | |
6 | Author: Frank da Cruz (fdc@watsun.cc.columbia.edu, FDCCU@CUVMA.BITNET), | |
7 | Columbia University Center for Computing Activities. | |
8 | First released January 1985. | |
9 | Copyright (C) 1985, 1992, Trustees of Columbia University in the City of New | |
10 | York. Permission is granted to any individual or institution to use this | |
11 | software as long as it is not sold for profit. This copyright notice must be | |
12 | retained. This software may not be included in commercial products without | |
13 | written permission of Columbia University. | |
14 | */ | |
15 | /* | |
16 | Note -- if you change this file, please amend the version number and date at | |
17 | the top of ckcfns.c accordingly. | |
18 | */ | |
19 | ||
20 | #include "ckcsym.h" /* Compilation options */ | |
21 | #include "ckcdeb.h" /* Debugging and other symbols */ | |
22 | #include "ckcasc.h" /* ASCII symbols */ | |
23 | #include "ckcker.h" /* Kermit symbols */ | |
24 | #include "ckcxla.h" /* Translation */ | |
25 | ||
26 | #ifdef TCPSOCKET /* For TELNET business in spack() */ | |
27 | #ifndef NP_TELNET | |
28 | #define NP_TELNET 1 | |
29 | extern int tn_nlm, ttnproto; | |
30 | #endif /* NP_TELNET */ | |
31 | #endif /* TCPSOCKET */ | |
32 | ||
33 | #ifdef DYNAMIC | |
34 | extern struct pktinfo *s_pkt; /* array of pktinfo structures */ | |
35 | extern struct pktinfo *r_pkt; /* array of pktinfo structures */ | |
36 | #else | |
37 | extern struct pktinfo s_pkt[]; /* array of pktinfo structures */ | |
38 | extern struct pktinfo r_pkt[]; /* array of pktinfo structures */ | |
39 | #endif /* DYNAMIC */ | |
40 | ||
41 | extern int sseqtbl[], rseqtbl[], sbufuse[], sacktbl[], wslots, winlo, sbufnum; | |
42 | ||
43 | extern int ttprty; /* from ckutio.c */ | |
44 | extern int autopar; | |
45 | ||
46 | extern int spsiz, spmax, rpsiz, timint, timef, npad, ebq, ebqflg; | |
47 | extern int rpt, rptq, rptflg, capas, spsizf, en_fin, tsecs, network, flow; | |
48 | extern int pktnum, sndtyp, bctr, bctu, bctl, rsn, rln, maxtry, size; | |
49 | extern int osize, maxsize, spktl, rpktl, nfils, stdouf, warn, parity; | |
50 | extern int turn, turnch, delay, displa, pktlog, tralog, seslog, xflg, mypadn; | |
51 | extern int hcflg, local, server, cxseen, czseen; | |
52 | extern int nakstate, quiet, success, xitsta, what; | |
53 | extern int spackets, rpackets, timeouts, retrans, crunched, urpsiz; | |
54 | ||
55 | extern long filcnt, ffc, flci, flco, tlci, tlco, tfc, speed; | |
56 | ||
57 | extern char *cmarg, *cmarg2, filnam[], *hlptxt; | |
58 | ||
59 | extern CHAR padch, mypadc, eol, seol, ctlq, myctlq, sstate; | |
60 | extern CHAR *recpkt, *data, encbuf[], myinit[]; | |
61 | extern CHAR *srvptr, stchr, mystch, *rdatap; | |
62 | extern CHAR padbuf[]; | |
63 | #ifdef DYNAMIC | |
64 | extern CHAR *srvcmd; | |
65 | #else | |
66 | extern CHAR srvcmd[]; | |
67 | #endif /* DYNAMIC */ | |
68 | ||
69 | int numerrs = 0; /* (PWP) total number packet errors so far */ | |
70 | ||
71 | static CHAR partab[] = { /* Even parity table for dopar(). */ | |
72 | (CHAR) '\000', /* ANSI C casts '\ooo' constants */ | |
73 | (CHAR) '\201', /* to signed char, so we have to */ | |
74 | (CHAR) '\202', /* cast back to unsigned char... */ | |
75 | (CHAR) '\003', | |
76 | (CHAR) '\204', | |
77 | (CHAR) '\005', | |
78 | (CHAR) '\006', | |
79 | (CHAR) '\207', | |
80 | (CHAR) '\210', | |
81 | (CHAR) '\011', | |
82 | (CHAR) '\012', | |
83 | (CHAR) '\213', | |
84 | (CHAR) '\014', | |
85 | (CHAR) '\215', | |
86 | (CHAR) '\216', | |
87 | (CHAR) '\017', | |
88 | (CHAR) '\220', | |
89 | (CHAR) '\021', | |
90 | (CHAR) '\022', | |
91 | (CHAR) '\223', | |
92 | (CHAR) '\024', | |
93 | (CHAR) '\225', | |
94 | (CHAR) '\226', | |
95 | (CHAR) '\027', | |
96 | (CHAR) '\030', | |
97 | (CHAR) '\231', | |
98 | (CHAR) '\232', | |
99 | (CHAR) '\033', | |
100 | (CHAR) '\234', | |
101 | (CHAR) '\035', | |
102 | (CHAR) '\036', | |
103 | (CHAR) '\237', | |
104 | (CHAR) '\240', | |
105 | (CHAR) '\041', | |
106 | (CHAR) '\042', | |
107 | (CHAR) '\243', | |
108 | (CHAR) '\044', | |
109 | (CHAR) '\245', | |
110 | (CHAR) '\246', | |
111 | (CHAR) '\047', | |
112 | (CHAR) '\050', | |
113 | (CHAR) '\251', | |
114 | (CHAR) '\252', | |
115 | (CHAR) '\053', | |
116 | (CHAR) '\254', | |
117 | (CHAR) '\055', | |
118 | (CHAR) '\056', | |
119 | (CHAR) '\257', | |
120 | (CHAR) '\060', | |
121 | (CHAR) '\261', | |
122 | (CHAR) '\262', | |
123 | (CHAR) '\063', | |
124 | (CHAR) '\264', | |
125 | (CHAR) '\065', | |
126 | (CHAR) '\066', | |
127 | (CHAR) '\267', | |
128 | (CHAR) '\270', | |
129 | (CHAR) '\071', | |
130 | (CHAR) '\072', | |
131 | (CHAR) '\273', | |
132 | (CHAR) '\074', | |
133 | (CHAR) '\275', | |
134 | (CHAR) '\276', | |
135 | (CHAR) '\077', | |
136 | (CHAR) '\300', | |
137 | (CHAR) '\101', | |
138 | (CHAR) '\102', | |
139 | (CHAR) '\303', | |
140 | (CHAR) '\104', | |
141 | (CHAR) '\305', | |
142 | (CHAR) '\306', | |
143 | (CHAR) '\107', | |
144 | (CHAR) '\110', | |
145 | (CHAR) '\311', | |
146 | (CHAR) '\312', | |
147 | (CHAR) '\113', | |
148 | (CHAR) '\314', | |
149 | (CHAR) '\115', | |
150 | (CHAR) '\116', | |
151 | (CHAR) '\317', | |
152 | (CHAR) '\120', | |
153 | (CHAR) '\321', | |
154 | (CHAR) '\322', | |
155 | (CHAR) '\123', | |
156 | (CHAR) '\324', | |
157 | (CHAR) '\125', | |
158 | (CHAR) '\126', | |
159 | (CHAR) '\327', | |
160 | (CHAR) '\330', | |
161 | (CHAR) '\131', | |
162 | (CHAR) '\132', | |
163 | (CHAR) '\333', | |
164 | (CHAR) '\134', | |
165 | (CHAR) '\335', | |
166 | (CHAR) '\336', | |
167 | (CHAR) '\137', | |
168 | (CHAR) '\140', | |
169 | (CHAR) '\341', | |
170 | (CHAR) '\342', | |
171 | (CHAR) '\143', | |
172 | (CHAR) '\344', | |
173 | (CHAR) '\145', | |
174 | (CHAR) '\146', | |
175 | (CHAR) '\347', | |
176 | (CHAR) '\350', | |
177 | (CHAR) '\151', | |
178 | (CHAR) '\152', | |
179 | (CHAR) '\353', | |
180 | (CHAR) '\154', | |
181 | (CHAR) '\355', | |
182 | (CHAR) '\356', | |
183 | (CHAR) '\157', | |
184 | (CHAR) '\360', | |
185 | (CHAR) '\161', | |
186 | (CHAR) '\162', | |
187 | (CHAR) '\363', | |
188 | (CHAR) '\164', | |
189 | (CHAR) '\365', | |
190 | (CHAR) '\366', | |
191 | (CHAR) '\167', | |
192 | (CHAR) '\170', | |
193 | (CHAR) '\371', | |
194 | (CHAR) '\372', | |
195 | (CHAR) '\173', | |
196 | (CHAR) '\374', | |
197 | (CHAR) '\175', | |
198 | (CHAR) '\176', | |
199 | (CHAR) '\377' | |
200 | }; | |
201 | ||
202 | /* CRC generation tables */ | |
203 | ||
204 | static long crcta[16] = { 0L, 010201L, 020402L, 030603L, 041004L, | |
205 | 051205L, 061406L, 071607L, 0102010L, 0112211L, 0122412L, 0132613L, 0143014L, | |
206 | 0153215L, 0163416L, 0173617L }; | |
207 | ||
208 | static long crctb[16] = { 0L, 010611L, 021422L, 031233L, 043044L, | |
209 | 053655L, 062466L, 072277L, 0106110L, 0116701L, 0127532L, 0137323L, 0145154L, | |
210 | 0155745L, 0164576L, 0174367L }; | |
211 | ||
212 | ||
213 | /* I N P U T -- Attempt to read packet number 'pktnum'. */ | |
214 | ||
215 | /* | |
216 | This is the function that feeds input to Kermit's finite state machine, | |
217 | in the form of a character in the range 32-126, normally a packet type | |
218 | (uppercase letter) or pseudo-packet-type (lowercase letter). | |
219 | ||
220 | If a special start state is in effect, that state is returned as if it were | |
221 | the type of an incoming packet. Otherwise: | |
222 | ||
223 | (fill in...) | |
224 | */ | |
225 | ||
226 | int | |
227 | input() { | |
228 | int type; | |
229 | int x = 0, y, k; | |
230 | ||
231 | debug(F101,"input sstate","",sstate); | |
232 | debug(F101," nakstate","",nakstate); | |
233 | debug(F000," sndtyp","",sndtyp); | |
234 | ||
235 | while (1) { /* Big loop... */ | |
236 | ||
237 | if (sstate != 0) { /* If a start state is in effect, */ | |
238 | type = sstate; /* return it like a packet type, */ | |
239 | sstate = 0; /* and then nullify it. */ | |
240 | numerrs = 0; /* (PWP) no errors so far */ | |
241 | return(type); | |
242 | } | |
243 | ||
244 | if (nakstate) { /* This section for file receiver. */ | |
245 | ||
246 | if (wslots > 1) { /* If we're doing windows, */ | |
247 | x = rseqtbl[winlo]; /* see if desired packet already in. */ | |
248 | debug(F101," winlo","",winlo); | |
249 | debug(F101," rseqtbl[winlo]","",rseqtbl[winlo]); | |
250 | if (x > -1) { /* Already there? */ | |
251 | if (r_pkt[x].pk_seq == winlo) { /* (double check) */ | |
252 | rsn = winlo; /* Yes, return its info */ | |
253 | debug(F101,"input return pre-stashed packet","",rsn); | |
254 | dumprbuf(); | |
255 | rdatap = r_pkt[x].pk_adr; /* like rpack would do. */ | |
256 | rln = (int)strlen((char *) rdatap); | |
257 | type = r_pkt[x].pk_typ; | |
258 | break; | |
259 | } | |
260 | } | |
261 | } | |
262 | type = rpack(); /* Try to read a packet. */ | |
263 | debug(F111,"input recv",(char *) rdatap,(int) type); | |
264 | while (type == 'e') { /* Handle echoes */ | |
265 | debug(F000,"echo discarded","",type); | |
266 | type = rpack(); | |
267 | } | |
268 | if (type < -1) return('q'); /* Ctrl-C */ | |
269 | if (type < 0) { /* Receive window full */ | |
270 | /* Another thing to do here would be to delete */ | |
271 | /* the highest packet and NAK winlo. But that */ | |
272 | /* shouldn't be necessary since the other Kermit */ | |
273 | /* should not have sent a packet outside the window. */ | |
274 | debug(F101,"rpack receive window full","",0); | |
275 | dumprbuf(); | |
276 | errpkt((CHAR *)"Receive window full."); | |
277 | strcpy((char *)recpkt,"Receive window full."); | |
278 | type = 'E'; | |
279 | break; | |
280 | } | |
281 | dumprbuf(); | |
282 | ||
283 | if (chkint() < 0) { /* Check for console interrupts. */ | |
284 | errpkt((CHAR *)"User cancelled."); | |
285 | strcpy((char *)recpkt,"User cancelled."); | |
286 | type = 'E'; | |
287 | break; | |
288 | } | |
289 | if (type == 'E') { | |
290 | debug(F101,"input got E, nakstate","",nakstate); | |
291 | break; /* Error packet */ | |
292 | } | |
293 | if (type == 'Q') { /* Crunched packet. */ | |
294 | crunched++; | |
295 | numerrs++; | |
296 | if (nack(winlo) < 0) { /* Request resend of window-low.. */ | |
297 | debug(F101,"input sent too many naks","",winlo); | |
298 | errpkt((CHAR *)"Too many retries."); | |
299 | strcpy((char *)recpkt,"Sent too many NAKs."); | |
300 | type = 'E'; | |
301 | break; | |
302 | } else continue; | |
303 | } | |
304 | if (type == 'T') { /* Timeout */ | |
305 | #ifdef BULKNAKS | |
306 | int z; | |
307 | #endif | |
308 | timeouts++; | |
309 | debug(F101,"input receive-state timeout, winlo","",winlo); | |
310 | #ifdef BULKNAKS | |
311 | z = winlo + wslots; /* NAK all unACK'd packets */ | |
312 | if (z > 63) z -= 64; | |
313 | debug(F101,"input sending bulk NAKs, winlo","",winlo); | |
314 | for (x = winlo; (x != z) && ttchk() == 0; x++) { | |
315 | if (x < 0 || x > 63) break; | |
316 | if (rseqtbl[x] < 0) { | |
317 | if (nack(x) < 0) { | |
318 | debug(F101,"input sent too many naks","",winlo); | |
319 | errpkt((CHAR *)"Too many retries."); | |
320 | strcpy(recpkt,"Sent too many NAKs."); | |
321 | type = 'E'; | |
322 | break; | |
323 | } | |
324 | } | |
325 | } | |
326 | #else /* NAK only the packet at window-low */ | |
327 | debug(F101,"input sending NAK for winlo","",winlo); | |
328 | if (nack(winlo) < 0) { | |
329 | debug(F101,"input sent too many naks","",winlo); | |
330 | errpkt((CHAR *)"Too many retries."); | |
331 | strcpy((char *)recpkt,"Sent too many NAKs."); | |
332 | type = 'E'; | |
333 | break; | |
334 | } | |
335 | #endif /* BULKNAKS */ | |
336 | continue; | |
337 | } | |
338 | ||
339 | /* Got the packet we want, done. */ | |
340 | ||
341 | if (rsn == winlo) { | |
342 | debug(F101,"input rsn=winlo","",rsn); | |
343 | break; | |
344 | } | |
345 | ||
346 | /* Got a packet out of order. */ | |
347 | ||
348 | debug(F101,"input recv got packet out of order","",rsn); | |
349 | k = rseqtbl[rsn]; /* Get window slot of this packet. */ | |
350 | debug(F101,"input recv rseqtbl[rsn]","",k); | |
351 | if (k < 0) { | |
352 | debug(F101,"input recv can't find index for rcvd pkt","",rsn); | |
353 | errpkt((CHAR *)"internal error number 21"); | |
354 | strcpy((char *)recpkt,"Sliding windows protocol error."); | |
355 | type = 'E'; | |
356 | break; | |
357 | } | |
358 | y = chkwin(rsn,winlo,wslots); /* See what window it's in. */ | |
359 | debug(F101,"input recv chkwin","",y); | |
360 | if (y == 1) { /* Previous window. */ | |
361 | #ifdef COMMENT | |
362 | ackn(rsn); /* Send empty ACK */ | |
363 | #else | |
364 | resend(rsn); | |
365 | #endif /* COMMENT */ | |
366 | freerpkt(rsn); /* Get rid of received packet */ | |
367 | continue; | |
368 | } else { /* In this window or out of range */ | |
369 | if (y < 0) /* If out of range entirely, */ | |
370 | freerpkt(rsn); /* release its buffer */ | |
371 | /* | |
372 | We have received a packet, but not the one we want. If we do nothing, | |
373 | we could be in for a lengthy timeout/retry cycle. It would seem to | |
374 | make sense to send a NAK for the most desired packet (winlo). But | |
375 | consider this scenario: a packet arrived damaged so we NAK'd it above; | |
376 | then packets winlo+1, winlo+2, ... winlo+n arrive, each one making us | |
377 | send a NAK for winlo, so the other Kermit gets n NAKs for winlo, and | |
378 | either would have to resend it n times, or if n > retry limit, give up | |
379 | because of too many retries. So we compromise: If a packet arrives | |
380 | that is not the most desired packet (winlo), we NAK winlo, BUT ONLY IF | |
381 | it has not been NAK'd before. | |
382 | */ | |
383 | if (s_pkt[k].pk_rtr == 0) { /* Have we been here before? */ | |
384 | if (nack(winlo) < 0) { /* No, NAK winlo. */ | |
385 | errpkt((CHAR *)"Too many retries."); /* Too many */ | |
386 | strcpy((char *)recpkt,"Timed out."); /* Give up */ | |
387 | type = 'E'; | |
388 | break; | |
389 | } else continue; | |
390 | } else continue; | |
391 | } | |
392 | /*!!!*/ | |
393 | } else { /* Otherwise file sender... */ | |
394 | ||
395 | if (wslots > 1) { /* Packet at winlo already ACK'd? */ | |
396 | if (sacktbl[winlo]) { /* If so, */ | |
397 | sacktbl[winlo] = 0; /* Turn off the ACK'd flag */ | |
398 | winlo = (winlo + 1) % 64; /* Rotate the window */ | |
399 | type = 'Y'; /* And return ACK */ | |
400 | debug(F101, | |
401 | "input send returning pre-stashed ACK","", | |
402 | winlo-1); | |
403 | break; | |
404 | } | |
405 | } | |
406 | type = rpack(); /* Try to read an acknowledgement */ | |
407 | debug(F111,"input send",(char *) rdatap,(int) type); | |
408 | while (type == 'e') { /* Handle echoes */ | |
409 | debug(F000,"echo discarded","",type); | |
410 | type = rpack(); | |
411 | } | |
412 | if (type == -2) return('q'); | |
413 | if (type == -1) { | |
414 | errpkt((CHAR *)"Internal error number 18"); | |
415 | debug(F101," wslots","",wslots); | |
416 | debug(F101," winlo","",winlo); | |
417 | debug(F101," pktnum","",pktnum); | |
418 | dumprbuf(); | |
419 | strcpy((char *)recpkt,"Can't allocate receive buffer"); | |
420 | type = 'E'; | |
421 | break; | |
422 | } | |
423 | dumprbuf(); /* debugging */ | |
424 | ||
425 | if (chkint() < 0) { /* Check for console interrupts. */ | |
426 | errpkt((CHAR *)"User cancelled."); | |
427 | strcpy((char *)recpkt,"User cancelled."); | |
428 | return(type = 'E'); | |
429 | } | |
430 | ||
431 | /* got a packet */ | |
432 | ||
433 | if (type == 'E') { | |
434 | debug(F101,"input send got E, nakstate","",nakstate); | |
435 | break; /* Error packet */ | |
436 | } | |
437 | if (type == 'Q') { /* Crunched packet */ | |
438 | crunched++; /* For statistics */ | |
439 | numerrs++; /* For packet resizing */ | |
440 | x = resend(winlo); /* Resend window-low */ | |
441 | if (x < 0) { | |
442 | type = 'E'; | |
443 | errpkt(recpkt); | |
444 | break; | |
445 | } | |
446 | continue; | |
447 | } | |
448 | if (type == 'T') { /* Timeout waiting for ACKs. */ | |
449 | timeouts++; /* Count it */ | |
450 | numerrs++; /* Count an error too */ | |
451 | debug(F101,"input send state timeout, winlo","",winlo); | |
452 | ||
453 | /* Retransmit the oldest un-ACK'd packet. */ | |
454 | ||
455 | debug(F101,"input send resending winlo","",winlo); | |
456 | if (resend(winlo) < 0) { /* Check retries */ | |
457 | debug(F101,"input send too many resends","",maxtry); | |
458 | errpkt(recpkt); | |
459 | return(type = 'E'); | |
460 | } | |
461 | continue; | |
462 | } | |
463 | ||
464 | /* Got an actual normal packet */ | |
465 | ||
466 | y = chkwin(rsn,winlo,wslots); /* Is it in the window? */ | |
467 | debug(F101,"input send rsn","",rsn); | |
468 | debug(F101,"input send winlo","",winlo); | |
469 | debug(F101,"input send chkwin","",y); | |
470 | if (type == 'Y') { /* Got an ACK */ | |
471 | if (y == 0) { /* In current window */ | |
472 | x = sseqtbl[rsn]; /* Mark the packet as ACK'd */ | |
473 | if (x > -1) s_pkt[x].pk_flg++; /* (old way) */ | |
474 | sacktbl[rsn]++; /* (new way) */ | |
475 | /* | |
476 | NOTE: The following statement frees the buffer of the ACK we just got. | |
477 | But the upper layers still need the data, like if it's the ACK to an I, | |
478 | S, F, D, Z, or just about any kind of packet. So for now, freerbuf() | |
479 | deallocates the buffer, but does not erase the data or destroy the pointer | |
480 | to it. There's no other single place where these receive buffers can be | |
481 | correctly freed (?) ... | |
482 | */ | |
483 | freerpkt(rsn); /* Free the ACK's buffer */ | |
484 | freesbuf(rsn); /* *** Free the sent packet's buffer */ | |
485 | if (rsn == winlo) { /* Got the one we want */ | |
486 | sacktbl[winlo] = 0; | |
487 | winlo = (winlo + 1) % 64; | |
488 | debug(F101,"input send rotated send window","",winlo); | |
489 | break; /* Return the ACK */ | |
490 | } else { | |
491 | debug(F101,"input send mark pkt","",rsn); | |
492 | continue; /* Otherwise go read another packet */ | |
493 | } | |
494 | } else { /* ACK not in window, ignore */ | |
495 | debug(F101,"input send ACK out of window","",rsn); | |
496 | freerpkt(rsn); | |
497 | continue; | |
498 | } | |
499 | } | |
500 | if (type == 'N') { /* NAK */ | |
501 | numerrs++; /* Count an error */ | |
502 | debug(F101,"input send NAK","",rsn); | |
503 | freerpkt(rsn); /* Free buffer where NAK lies. */ | |
504 | if (y == 0) { /* In current window */ | |
505 | debug(F100," in window","",0); | |
506 | k = sseqtbl[rsn]; /* Get pointer to NAK'd packet. */ | |
507 | x = 0; | |
508 | if (k < 0 || (k > -1 && s_pkt[k].pk_typ == ' ')) { | |
509 | x = resend(winlo); /* Packet we haven't sent yet. */ | |
510 | } else { | |
511 | x = resend(rsn); /* Resend requested packet. */ | |
512 | } | |
513 | if (x < 0) { /* Resend error is fatal. */ | |
514 | type = 'E'; | |
515 | errpkt(recpkt); | |
516 | break; | |
517 | } else continue; /* Resend ok, go read another packet */ | |
518 | } else if ((rsn == (pktnum + 1) % 64)) { /* NAK for next pkt */ | |
519 | if (wslots > 1) { | |
520 | debug( F101,"NAK for next packet, windowing","",rsn); | |
521 | x = resend(winlo); /* Resend window-low */ | |
522 | if (x < 0) { | |
523 | type = 'E'; | |
524 | errpkt(recpkt); | |
525 | break; | |
526 | } | |
527 | continue; /* Go back and read another pkt */ | |
528 | } | |
529 | debug(F101,"NAK for next packet, no windowing","",rsn); | |
530 | x = (rsn == 0) ? 63 : rsn - 1; | |
531 | if (x == 0 && (sndtyp == 'S' || sndtyp == 'I')) { | |
532 | resend(0); /* ACK for S or I packet missing */ | |
533 | continue; /* so resend it. */ | |
534 | } /* Else, treat NAK(n+1) as ACK(n) */ | |
535 | if ((x = sseqtbl[x]) > -1) { | |
536 | sacktbl[x]++; /* (new way) */ | |
537 | s_pkt[x].pk_flg++; /* (old way) */ | |
538 | } | |
539 | type = 'Y'; /* Treat it as ACK for last pkt */ | |
540 | break; | |
541 | } else if (y > 0) { /* NAK for pkt we can't resend */ | |
542 | debug(F101," NAK out of window","",rsn); /* bad... */ | |
543 | type = 'E'; | |
544 | errpkt((CHAR *)"NAK out of window"); | |
545 | strcpy((char *)recpkt,"NAK out of window."); | |
546 | break; | |
547 | } else continue; /* Ignore other NAKs */ | |
548 | } /* End of file-sender NAK handler */ | |
549 | ||
550 | if (rsn == winlo) { /* Not ACK, NAK, timeout, etc. */ | |
551 | debug(F000,"input send unexpected type","",type); | |
552 | break; | |
553 | } | |
554 | } /* End of file-sender section */ | |
555 | } /* End of input() loop */ | |
556 | if (wslots == 1) { | |
557 | debug(F100,"input about to flush","",0); | |
558 | ttflui(); /* Got what we want, clear input buffer. */ | |
559 | } | |
560 | if (!nakstate) /* When sending */ | |
561 | rcalcpsz(); /* recalculate size every packet */ | |
562 | debug(F000,"input returning type","",type); | |
563 | return(type); /* Success, return packet type. */ | |
564 | } | |
565 | \f | |
566 | /* D O P A R -- Add an appropriate parity bit to a character */ | |
567 | ||
568 | /* | |
569 | (PWP) this is still used in the Mac terminal emulator, so we have to keep it | |
570 | */ | |
571 | CHAR | |
572 | #ifdef CK_ANSIC | |
573 | dopar(register CHAR ch) | |
574 | #else | |
575 | dopar(ch) register CHAR ch; | |
576 | #endif /* CK_ANSIC */ | |
577 | { | |
578 | register unsigned int a; | |
579 | if (!parity) return((CHAR) (ch & 255)); else a = ch & 127; | |
580 | switch (parity) { | |
581 | case 'e': return(partab[a]); /* Even */ | |
582 | case 'm': return((CHAR) (a | 128)); /* Mark */ | |
583 | case 'o': return((CHAR) (partab[a] ^ 128)); /* Odd */ | |
584 | case 's': return((CHAR) a); /* Space */ | |
585 | default: return((CHAR) a); /* Something illegal */ | |
586 | } | |
587 | } | |
588 | ||
589 | #ifdef PARSENSE | |
590 | /* P A R C H K -- Check if Kermit packet has parity */ | |
591 | ||
592 | /* | |
593 | Call with s = pointer to packet, start = packet start character, n = length. | |
594 | Returns 0 if packet has no parity, -1 on error, or, if packet has parity: | |
595 | 'e' for even, 'o' for odd, 'm' for mark. Space parity cannot be sensed. | |
596 | So a return value of 0 really means either space or none. | |
597 | Returns -2 if parity has already been checked during this protocol operation. | |
598 | */ | |
599 | int | |
600 | #ifdef CK_ANSIC | |
601 | parchk(CHAR *s, CHAR start, int n) | |
602 | #else | |
603 | parchk(s,start,n) CHAR *s, start; int n; | |
604 | #endif /* CK_ANSIC */ | |
605 | /* parchk */ { | |
606 | CHAR s0, s1, s2, s3; | |
607 | ||
608 | debug(F101,"parchk n","",n); | |
609 | debug(F101,"parchk start","",start); | |
610 | ||
611 | s0 = s[0] & 0x7f; /* Mark field (usually Ctrl-A) */ | |
612 | ||
613 | if (s0 != start || n < 5) return(-1); /* Not a valid packet */ | |
614 | ||
615 | /* Look at packet control fields, which never have 8th bit set */ | |
616 | /* First check for no parity, most common case. */ | |
617 | ||
618 | if (((s[0] | s[1] | s[2] | s[3]) & 0x80) == 0) | |
619 | return(0); /* No parity or space parity */ | |
620 | ||
621 | /* Check for mark parity */ | |
622 | ||
623 | if (((s[0] & s[1] & s[2] & s[3]) & 0x80) == 0x80) | |
624 | return('m'); /* Mark parity */ | |
625 | ||
626 | /* Packet has some kind of parity */ | |
627 | /* Make 7-bit copies of control fields */ | |
628 | ||
629 | s1 = s[1] & 0x7f; /* LEN */ | |
630 | s2 = s[2] & 0x7f; /* SEQ */ | |
631 | s3 = s[3] & 0x7f; /* TYPE */ | |
632 | ||
633 | /* Check for even parity */ | |
634 | ||
635 | if ((s[0] == partab[s0]) && | |
636 | (s[1] == partab[s1]) && | |
637 | (s[2] == partab[s2]) && | |
638 | (s[3] == partab[s3])) | |
639 | return('e'); | |
640 | ||
641 | /* Check for odd parity */ | |
642 | ||
643 | if ((s[0] != partab[s0]) && | |
644 | (s[1] != partab[s1]) && | |
645 | (s[2] != partab[s2]) && | |
646 | (s[3] != partab[s3])) | |
647 | return('o'); | |
648 | ||
649 | /* Otherwise it's probably line noise. Let checksum calculation catch it. */ | |
650 | ||
651 | return(-1); | |
652 | } | |
653 | #endif /* PARSENSE */ | |
654 | \f | |
655 | /* | |
656 | Check to make sure timeout intervals are long enough to allow maximum | |
657 | length packets to get through before the timer goes off. If not, the | |
658 | timeout interval is adjusted upwards. | |
659 | ||
660 | This routine is called at the beginning of a transaction, before we | |
661 | know anything about the delay characteristics of the line. It works | |
662 | only for serial communication devices; it trusts the speed reported by | |
663 | the operating system. | |
664 | ||
665 | Call with a timout interval. Returns it, adjusted if necessary. | |
666 | */ | |
667 | int | |
668 | chktimo(timo,flag) int timo, flag; { | |
669 | long cps, z; int x, y; | |
670 | debug(F101,"chktimo timo","",timo); /* Timeout before adjustment */ | |
671 | debug(F101,"chktimo flag","",flag); | |
672 | ||
673 | if (flag) /* Don't change timeout if user */ | |
674 | return(timo); /* gave SET SEND TIMEOUT command. */ | |
675 | debug(F101,"chktimo spmax","",spmax); | |
676 | debug(F101,"chktimo urpsiz","",urpsiz); | |
677 | ||
678 | speed = ttgspd(); /* Get current speed. */ | |
679 | if (speed > 0L && !network) { | |
680 | cps = speed / 10L; /* Convert to chars per second */ | |
681 | if (cps > 0L) { | |
682 | long plen; /* Maximum of send and rcv pkt size */ | |
683 | z = cps * (long) timo; /* Chars per timeout interval */ | |
684 | z -= z / 10L; /* Less 10 percent */ | |
685 | plen = spmax; | |
686 | if (urpsiz > spmax) plen = urpsiz; | |
687 | debug(F101,"chktimo plen","",plen); | |
688 | if (z < plen) { /* Compare with packet size */ | |
689 | x = (int) ((long) plen / cps); /* Adjust if necessary */ | |
690 | y = x / 10; /* Add 10 percent for safety */ | |
691 | if (y < 2) y = 2; /* Or 2 seconds, whichever is more */ | |
692 | x += y; | |
693 | if (x > timo) /* If this is greater than current */ | |
694 | timo = x; /* timeout, change the timeout */ | |
695 | debug(F101,"chktimo new timo","",timo); | |
696 | } | |
697 | } | |
698 | } | |
699 | return(timo); | |
700 | } | |
701 | \f | |
702 | /* S P A C K -- Construct and send a packet */ | |
703 | ||
704 | /* | |
705 | spack() sends a packet of the given type, sequence number n, with len data | |
706 | characters pointed to by d, in either a regular or extended- length packet, | |
707 | depending on len. Returns the number of bytes actually sent, or else -1 | |
708 | upon failure. Uses global npad, padch, mystch, bctu, data. Leaves packet | |
709 | fully built and null-terminated for later retransmission by resend(). | |
710 | Updates global sndpktl (send-packet length). | |
711 | ||
712 | NOTE: The global pointer "data" is assumed to point into the 7th position | |
713 | of a character array (presumably in packet buffer for the current packet). | |
714 | It was used by getpkt() to build the packet data field. spack() fills in | |
715 | the header to the left of the data pointer (the data pointer is defined | |
716 | in getsbuf() in ckcfn3.c). If the address "d" is the same as "data", then | |
717 | the packet's data field has been built "in place" and need not be copied. | |
718 | */ | |
719 | int | |
720 | #ifdef CK_ANSIC | |
721 | spack(char pkttyp, int n, int len, CHAR *d) | |
722 | #else | |
723 | spack(pkttyp,n,len,d) char pkttyp; int n, len; CHAR *d; | |
724 | #endif /* CK_ANSIC */ | |
725 | /* spack */ { | |
726 | register int i; | |
727 | int j, k, lp, longpkt, copy; | |
728 | register CHAR *cp, *mydata; | |
729 | unsigned crc; | |
730 | ||
731 | debug(F101,"spack n","",n); | |
732 | debug(F111," data",data,data); | |
733 | debug(F101," d","",d); | |
734 | debug(F101," len","",len); | |
735 | ||
736 | copy = (d != data); /* Flag whether data must be copied */ | |
737 | longpkt = (len + bctl + 2) > 94; /* Decide whether it's a long packet */ | |
738 | mydata = data - 7 + (longpkt ? 0 : 3); /* Starting position of header */ | |
739 | debug(F101," mydata","",mydata); | |
740 | ||
741 | k = sseqtbl[n]; /* Packet structure info for pkt n */ | |
742 | debug(F101," sseqtbl[n]","",k); | |
743 | if (k < 0) { | |
744 | debug(F101,"spack sending packet out of window","",n); | |
745 | } else { /* Record packet info */ | |
746 | s_pkt[k].pk_adr = mydata; /* Remember address of packet. */ | |
747 | s_pkt[k].pk_seq = n; /* Record sequence number */ | |
748 | s_pkt[k].pk_typ = pkttyp; /* Record packet type */ | |
749 | } | |
750 | ||
751 | spktl = 0; /* Initialize length of this packet */ | |
752 | i = 0; /* and position in packet. */ | |
753 | ||
754 | /* Now fill the packet */ | |
755 | ||
756 | mydata[i++] = mystch; /* MARK */ | |
757 | lp = i++; /* Position of LEN, fill in later */ | |
758 | ||
759 | mydata[i++] = tochar(n); /* SEQ field */ | |
760 | mydata[i++] = pkttyp; /* TYPE field */ | |
761 | j = len + bctl; /* Length of data + block check */ | |
762 | if (longpkt) { /* Long packet? */ | |
763 | int x; /* Work around SCO Xenix/286 */ | |
764 | x = 95; /* compiler bug... */ | |
765 | x = j / 95; | |
766 | mydata[lp] = tochar(0); /* Yes, set LEN to zero */ | |
767 | mydata[i++] = tochar(x); /* High part */ | |
768 | mydata[i++] = tochar(j % 95); /* Low part */ | |
769 | mydata[i] = '\0'; /* Header checksum */ | |
770 | mydata[i++] = tochar(chk1(mydata+lp)); | |
771 | } else mydata[lp] = tochar(j+2); /* Normal LEN */ | |
772 | ||
773 | if (copy) /* Data field built in place? */ | |
774 | for ( ; len--; i++) mydata[i] = *d++; /* No, must copy. */ | |
775 | else /* Otherwise, */ | |
776 | i += len; /* Just skip past data field. */ | |
777 | mydata[i] = '\0'; /* Null-terminate for checksum calc. */ | |
778 | ||
779 | switch (bctu) { /* Block check */ | |
780 | case 1: /* 1 = 6-bit chksum */ | |
781 | mydata[i++] = tochar(chk1(mydata+lp)); | |
782 | break; | |
783 | case 2: /* 2 = 12-bit chksum */ | |
784 | j = chk2(mydata+lp); | |
785 | mydata[i++] = (unsigned)tochar((j >> 6) & 077); | |
786 | mydata[i++] = (unsigned)tochar(j & 077); | |
787 | break; | |
788 | case 3: /* 3 = 16-bit CRC */ | |
789 | crc = chk3(mydata+lp); | |
790 | mydata[i++] = (unsigned)tochar(((crc & 0170000)) >> 12); | |
791 | mydata[i++] = (unsigned)tochar((crc >> 6) & 077); | |
792 | mydata[i++] = (unsigned)tochar(crc & 077); | |
793 | break; | |
794 | case 4: /* 2 = 12-bit chksum, blank-free */ | |
795 | j = chk2(mydata+lp); | |
796 | mydata[i++] = | |
797 | (unsigned)(tochar((unsigned)(((j >> 6) & 077) + 1))); | |
798 | mydata[i++] = (unsigned)(tochar((unsigned)((j & 077) + 1))); | |
799 | break; | |
800 | } | |
801 | mydata[i++] = seol; /* End of line (packet terminator) */ | |
802 | #ifdef TCPSOCKET | |
803 | /* | |
804 | If TELNET connection and packet terminator is carriage return, | |
805 | we must stuff either LF or NUL, according to SET TELNET NEWLINE-MODE | |
806 | (tn_nlm), to meet the TELNET specification. | |
807 | */ | |
808 | if (network && ttnproto == NP_TELNET && seol == CR) | |
809 | mydata[i++] = tn_nlm ? LF : NUL; | |
810 | #endif /* TCPSOCKET */ | |
811 | mydata[i] = '\0'; /* Terminate string */ | |
812 | logpkt('s',n,mydata); /* Log packet */ | |
813 | ||
814 | /* (PWP) add the parity quickly at the end */ | |
815 | switch (parity) { | |
816 | case 'e': /* Even */ | |
817 | for (cp = &mydata[i-1]; cp >= mydata; cp--) | |
818 | *cp = partab[*cp]; | |
819 | break; | |
820 | case 'm': /* Mark */ | |
821 | for (cp = &mydata[i-1]; cp >= mydata; cp--) | |
822 | *cp |= 128; | |
823 | break; | |
824 | case 'o': /* Odd */ | |
825 | for (cp = &mydata[i-1]; cp >= mydata; cp--) | |
826 | *cp = partab[*cp] ^ 128; | |
827 | break; | |
828 | case 's': /* Space */ | |
829 | for (cp = &mydata[i-1]; cp >= mydata; cp--) | |
830 | *cp &= 127; | |
831 | break; | |
832 | } | |
833 | if (npad) ttol(padbuf,npad); /* Send any padding */ | |
834 | spktl = i; /* Remember packet length */ | |
835 | s_pkt[k].pk_len = spktl; /* also in packet info structure */ | |
836 | if (ttol(mydata,spktl) < 0) return(-1); /* Send the packet */ | |
837 | sndtyp = pkttyp; /* Remember packet type for echos */ | |
838 | spackets++; /* Count it. */ | |
839 | flco += spktl; /* Count the characters */ | |
840 | tlco += spktl; /* for statistics... */ | |
841 | dumpsbuf(); /* Dump send buffers to debug log */ | |
842 | screen(SCR_PT,pkttyp,(long)n,(char *)mydata); /* Update screen */ | |
843 | return(spktl); /* Return length */ | |
844 | } | |
845 | \f | |
846 | /* C H K 1 -- Compute a type-1 Kermit 6-bit checksum. */ | |
847 | ||
848 | int | |
849 | chk1(pkt) register CHAR *pkt; { | |
850 | register unsigned int chk; | |
851 | chk = chk2(pkt); | |
852 | chk = (((chk & 0300) >> 6) + chk) & 077; | |
853 | return((int) chk); | |
854 | } | |
855 | ||
856 | /* C H K 2 -- Compute the numeric sum of all the bytes in the packet. */ | |
857 | ||
858 | unsigned int | |
859 | chk2(pkt) register CHAR *pkt; { | |
860 | register long chk; register unsigned int m; | |
861 | m = (parity) ? 0177 : 0377; | |
862 | for (chk = 0; *pkt != '\0'; pkt++) | |
863 | chk += *pkt & m; | |
864 | return((unsigned int) (chk & 07777)); | |
865 | } | |
866 | ||
867 | ||
868 | /* C H K 3 -- Compute a type-3 Kermit block check. */ | |
869 | /* | |
870 | Calculate the 16-bit CRC-CCITT of a null-terminated string using a lookup | |
871 | table. Assumes the argument string contains no embedded nulls. | |
872 | */ | |
873 | unsigned int | |
874 | chk3(pkt) register CHAR *pkt; { | |
875 | register long c, crc; | |
876 | register unsigned int m; | |
877 | m = (parity) ? 0177 : 0377; | |
878 | for (crc = 0; *pkt != '\0'; pkt++) { | |
879 | c = crc ^ (long)(*pkt & m); | |
880 | crc = (crc >> 8) ^ (crcta[(c & 0xF0) >> 4] ^ crctb[c & 0x0F]); | |
881 | } | |
882 | return((unsigned int) (crc & 0xFFFF)); | |
883 | } | |
884 | \f | |
885 | int | |
886 | nxtpkt() { /* Called by file sender */ | |
887 | int j, n; | |
888 | ||
889 | debug(F101,"nxtpkt pktnum","",pktnum); | |
890 | debug(F101,"nxtpkt winlo ","",winlo); | |
891 | n = (pktnum + 1) % 64; /* Increment packet number mod 64 */ | |
892 | #ifdef COMMENT | |
893 | /* | |
894 | Suggested by Alan Grieg. A packet can be sent out of window in | |
895 | circumstances involving acks received out of order, ... Have to think | |
896 | about this... | |
897 | */ | |
898 | if (chkwin(n,winlo,wslots)) { | |
899 | debug(F101,"nxtpkt n not in window","",n); | |
900 | return(-1); | |
901 | } | |
902 | #endif | |
903 | j = getsbuf(n); /* Get a buffer for packet n */ | |
904 | if (j < 0) { | |
905 | debug(F101,"nxtpkt can't getsbuf","",j); | |
906 | return(-1); | |
907 | } | |
908 | pktnum = n; | |
909 | debug(F101,"nxtpkt bumped pktnum to","",pktnum); | |
910 | return(0); | |
911 | } | |
912 | ||
913 | /* Functions for sending ACKs and NAKs */ | |
914 | ||
915 | /* Note, we should only ACK the packet at window-low (winlo) */ | |
916 | /* However, if an old packet arrives again (e.g. because the ACK we sent */ | |
917 | /* earlier was lost), we ACK it again. */ | |
918 | ||
919 | int | |
920 | ack() { /* Acknowledge the current packet. */ | |
921 | return(ackns(winlo,(CHAR *)"")); | |
922 | } | |
923 | ||
924 | int | |
925 | ackns(n,s) int n; CHAR *s; { /* Acknowledge packet n */ | |
926 | int j, k; | |
927 | debug(F111,"ackns",s,n); | |
928 | ||
929 | k = rseqtbl[n]; /* First find received packet n. */ | |
930 | debug(F101,"ackns k","",k); | |
931 | #ifdef COMMENT | |
932 | /* No need to set ACK'd bit, because we're gonna free the buffer now */ | |
933 | if (k > -1) /* If in window */ | |
934 | s_pkt[k].pk_flg++; /* mark the ack'd bit. */ | |
935 | else | |
936 | debug(F101,"ackns can't set ack'd bit","",k); | |
937 | #endif | |
938 | freesbuf(n); /* Free current send-buffer, if any */ | |
939 | if ((j = getsbuf(n)) < 0) { | |
940 | /* This can happen if we have to re-ACK an old packet that has */ | |
941 | /* already left the window. It does no harm. */ | |
942 | debug(F101,"ackns can't getsbuf","",n); | |
943 | } | |
944 | spack('Y',n,(int)strlen((char *)s),s); /* Now send it. */ | |
945 | debug(F101,"ackns winlo","",winlo); | |
946 | debug(F101,"ackns n","",n); | |
947 | if (n == winlo) { /* If we're acking winlo */ | |
948 | if (k > -1) | |
949 | freerbuf(k); /* don't need it any more */ | |
950 | if (j > -1) | |
951 | freesbuf(j); /* and don't need to keep ACK either */ | |
952 | winlo = (winlo + 1) % 64; | |
953 | } | |
954 | return(0); | |
955 | } | |
956 | ||
957 | int | |
958 | ackn(n) int n; { /* Send ACK for packet number n */ | |
959 | return(ackns(n,(CHAR *)"")); | |
960 | } | |
961 | ||
962 | int | |
963 | ack1(s) CHAR *s; { /* Send an ACK with data. */ | |
964 | debug(F110,"ack1",(char *) s,0); | |
965 | return(ackns(winlo, s)); | |
966 | } | |
967 | ||
968 | /* N A C K -- Send a Negative ACKnowledgment. */ | |
969 | /* | |
970 | Call with the packet number, n, to be NAK'd. | |
971 | Returns -1 if that packet has been NAK'd too many times, otherwise 0. | |
972 | Btw, it is not right to return 0 under error conditions. This is | |
973 | done because the -1 code is used for cancelling the file transfer. | |
974 | More work is needed here. | |
975 | */ | |
976 | int | |
977 | nack(n) int n; { | |
978 | int i; | |
979 | ||
980 | if (n < 0 || n > 63) { | |
981 | debug(F101,"nack bad pkt num","",n); | |
982 | return(0); | |
983 | } else debug(F101,"nack","",n); | |
984 | if ((i = sseqtbl[n]) < 0) { /* If necessary */ | |
985 | if (getsbuf(n) < 0) { /* get a buffer for this NAK */ | |
986 | debug(F101,"nack can't getsbuf","",n); | |
987 | return(0); | |
988 | } else i = sseqtbl[n]; /* New slot number */ | |
989 | } | |
990 | if (s_pkt[i].pk_rtr++ > maxtry) /* How many times have we done this? */ | |
991 | return(-1); /* Too many... */ | |
992 | ||
993 | /* Note, don't free this buffer. Eventually an ACK will come, and that */ | |
994 | /* will set it free. If not, well, it's back to ground zero anyway... */ | |
995 | ||
996 | spack('N',n,0,(CHAR *) ""); /* NAKs never have data. */ | |
997 | return(0); | |
998 | } | |
999 | ||
1000 | /* | |
1001 | * (PWP) recalculate the optimal packet length in the face of errors. | |
1002 | * This is a modified version of the algorithm by John Chandler in Kermit/370, | |
1003 | * see "Dynamic Packet Size Control", Kermit News, V2 #1, June 1988. | |
1004 | * | |
1005 | * This implementation minimizes the total overhead equation, which is | |
1006 | * | |
1007 | * Total chars = file_chars + (header_len * num_packs) | |
1008 | * + (errors * (header_len + packet_len)) | |
1009 | * | |
1010 | * Differentiate with respect to number of chars, solve for packet_len, get: | |
1011 | * | |
1012 | * packet_len = sqrt (file_chars * header_len / errors) | |
1013 | */ | |
1014 | ||
1015 | /* | |
1016 | (FDC) New super-simple algorithm. If there was an error in the most recent | |
1017 | packet exchange, cut the send-packet size in half, down to a minimum of 20. | |
1018 | If there was no error, increase the size by 5/4, up to the maximum negotiated | |
1019 | length. Seems to be much more responsive than previous algorithm, which took | |
1020 | forever to recover the original packet length, and it also went crazy under | |
1021 | certain conditions. | |
1022 | ||
1023 | Here's another idea for packet length resizing that keeps a history of the | |
1024 | last n packets. Push a 1 into the left end of an n-bit shift register if the | |
1025 | current packet is good, otherwise push a zero. The current n-bit value, w, of | |
1026 | this register is a weighted sum of the noise hits for the last n packets, with | |
1027 | the most recent weighing the most. The current packet length is some function | |
1028 | of w and the negotiated packet length, like: | |
1029 | ||
1030 | (2^n - w) / (2^n) * (negotiated length) | |
1031 | ||
1032 | If the present resizing method causes problems, think about this one a little | |
1033 | more. | |
1034 | */ | |
1035 | VOID | |
1036 | rcalcpsz() { | |
1037 | ||
1038 | #ifdef COMMENT | |
1039 | /* Old way */ | |
1040 | register long x, q; | |
1041 | if (numerrs == 0) return; /* bounds check just in case */ | |
1042 | ||
1043 | /* overhead on a data packet is npad+5+bctr, plus 3 if extended packet */ | |
1044 | /* an ACK is 5+bctr */ | |
1045 | ||
1046 | /* first set x = per packet overhead */ | |
1047 | if (wslots > 1) | |
1048 | x = (long) (npad+5+bctr); /* only the packet, don't count the ack */ | |
1049 | else | |
1050 | x = (long) (npad+5+3+bctr+5+bctr); | |
1051 | ||
1052 | /* then set x = packet length ** 2 */ | |
1053 | x = x * ( ffc / (long) numerrs); /* careful of overflow */ | |
1054 | ||
1055 | /* calculate the long integer sqrt(x) quickly */ | |
1056 | q = 500; | |
1057 | q = (q + x/q) >> 1; | |
1058 | q = (q + x/q) >> 1; | |
1059 | q = (q + x/q) >> 1; | |
1060 | q = (q + x/q) >> 1; /* should converge in about 4 steps */ | |
1061 | if ((q > 94) && (q < 130)) /* break-even point for long packets */ | |
1062 | q = 94; | |
1063 | if (q > spmax) q = spmax; /* maximum bounds */ | |
1064 | if (q < 10) q = 10; /* minimum bounds */ | |
1065 | spsiz = q; /* set new send packet size */ | |
1066 | debug(F101,"rcalcpsiz","",q); | |
1067 | #else | |
1068 | /* New way */ | |
1069 | if (spackets < 3) return; | |
1070 | debug(F101,"rcalcpsiz numerrs","",numerrs); | |
1071 | debug(F101,"rcalcpsiz spsiz","",spsiz); | |
1072 | if (numerrs) | |
1073 | spsiz = spsiz / 2; | |
1074 | else | |
1075 | spsiz = (spsiz / 4) * 5; | |
1076 | if (spsiz < 20) spsiz = 20; | |
1077 | if (spsiz > spmax) spsiz = spmax; | |
1078 | debug(F101,"rcalcpsiz new spsiz","",spsiz); | |
1079 | numerrs = 0; | |
1080 | return; | |
1081 | #endif | |
1082 | } | |
1083 | ||
1084 | /* R E S E N D -- Retransmit packet n. */ | |
1085 | ||
1086 | /* | |
1087 | Returns 0 or positive on success (the number of retries for packet n). | |
1088 | On failure, returns a negative number, and an error message is placed | |
1089 | in recpkt. | |
1090 | */ | |
1091 | int | |
1092 | resend(n) int n; { /* Send packet n again. */ | |
1093 | int j, k; | |
1094 | ||
1095 | debug(F101,"resend seq","",n); | |
1096 | ||
1097 | k = chkwin(n,winlo,wslots); /* See if packet in current window */ | |
1098 | j = -1; /* Assume it's lost */ | |
1099 | if (k == 0) j = sseqtbl[n]; /* See if we still have a copy of it */ | |
1100 | if (k != 0 || j < 0) { /* If not.... */ | |
1101 | if (nakstate && k == 1) { | |
1102 | /* | |
1103 | Packet n is in the previous window and we are the file receiver. | |
1104 | We already sent the ACK and deallocated its buffer so we can't just | |
1105 | retransmit the ACK. Rather than give up, we try some tricks... | |
1106 | */ | |
1107 | if (n == 0 && spackets < 63 && myinit[0]) { /* ACK to Send-Init */ | |
1108 | /* | |
1109 | If the packet number is 0, and we're at the beginning of a protocol | |
1110 | operation (spackets < 63), then we have to resend the ACK to an I or S | |
1111 | packet, complete with parameters in the data field. So we take a chance and | |
1112 | send a copy of the parameters in an ACK packet with block check type 1. | |
1113 | */ | |
1114 | int bctlsav; /* Temporary storage */ | |
1115 | int bctusav; | |
1116 | bctlsav = bctl; /* Save current block check length */ | |
1117 | bctusav = bctu; /* and type */ | |
1118 | bctu = bctl = 1; /* Set block check to 1 */ | |
1119 | spack('Y',0,(int)strlen((char *)myinit),(CHAR *)myinit); | |
1120 | logpkt('#',n,(CHAR *)"<reconstructed>"); /* Log it */ | |
1121 | bctu = bctusav; /* Restore block check type */ | |
1122 | bctl = bctlsav; /* and length */ | |
1123 | ||
1124 | } else { /* Not the first packet */ | |
1125 | /* | |
1126 | It's not the first packet of the protocol operation. It's some other packet | |
1127 | that we have already ACK'd and forgotten about. So we take a chance and | |
1128 | send an empty ACK using the current block-check type. Usually this will | |
1129 | work out OK (like when acking Data packets), and no great harm will be done | |
1130 | if it was some other kind of packet (F, etc). If we are requesting an | |
1131 | interruption of the file transfer, the flags are still set, so we'll catch | |
1132 | up on the next packet. | |
1133 | */ | |
1134 | spack('Y',n,0,(CHAR *) ""); | |
1135 | logpkt('#',n,(CHAR *)"<faith>"); /* Log it */ | |
1136 | } | |
1137 | retrans++; | |
1138 | screen(SCR_PT,'%',(long)pktnum,"(resend)"); | |
1139 | return(0); | |
1140 | } else { | |
1141 | /* | |
1142 | Packet number is not in current or previous window. We seem to hit this | |
1143 | code occasionally at the beginning of a transaction, for apparently no good | |
1144 | reason. Let's just log it for debugging, send nothing, and try to proceed | |
1145 | with the protocol rather than killing it. | |
1146 | */ | |
1147 | debug(F101,"RESEND PKT NOT IN WINDOW","",n); | |
1148 | debug(F101,"RESEND k","",k); | |
1149 | #ifdef COMMENT | |
1150 | sprintf((char *)recpkt, | |
1151 | " resend error: NIW, n=%d, k=%d.",n,k); | |
1152 | return(-2); | |
1153 | #else | |
1154 | return(0); | |
1155 | #endif /* COMMENT */ | |
1156 | } | |
1157 | } | |
1158 | ||
1159 | /* OK, it's in the window and it's not lost. */ | |
1160 | ||
1161 | debug(F101,"resend pktinfo index","",k); | |
1162 | ||
1163 | if (s_pkt[j].pk_rtr++ > maxtry) { /* Found it but over retry limit */ | |
1164 | strcpy((char *)recpkt,"Too many retries."); | |
1165 | return(-1); | |
1166 | } | |
1167 | debug(F101," retry","",s_pkt[j].pk_rtr); /* OK so far */ | |
1168 | dumpsbuf(); /* (debugging) */ | |
1169 | if (s_pkt[j].pk_typ == ' ') { /* Incompletely formed packet */ | |
1170 | if (nakstate) { /* (This shouldn't happen any more) */ | |
1171 | nack(n); | |
1172 | retrans++; | |
1173 | screen(SCR_PT,'%',(long)pktnum,"(resend)"); | |
1174 | return(s_pkt[j].pk_rtr); | |
1175 | } else { /* No packet to resend! */ | |
1176 | #ifdef COMMENT | |
1177 | /* | |
1178 | This happened (once) while sending a file with 2 window slots and typing | |
1179 | X to the sender to cancel the file. But since we're cancelling anyway, | |
1180 | no need to give a scary message. | |
1181 | */ | |
1182 | sprintf((char *)recpkt, | |
1183 | "resend logic error: NPS, n=%d, j=%d.",n,j); | |
1184 | return(-2); | |
1185 | #else | |
1186 | /* Just ignore it. */ | |
1187 | return(0); | |
1188 | #endif /* COMMENT */ | |
1189 | } | |
1190 | } | |
1191 | ttol(s_pkt[j].pk_adr,s_pkt[j].pk_len); /* Everything ok, send the packet */ | |
1192 | retrans++; /* Count a retransmission */ | |
1193 | screen(SCR_PT,'%',(long)pktnum,"(resend)"); /* Tell user about resend */ | |
1194 | logpkt('S',n,s_pkt[j].pk_adr); /* Log the resent packet */ | |
1195 | return(s_pkt[j].pk_rtr); /* Return the number of retries. */ | |
1196 | } | |
1197 | ||
1198 | int | |
1199 | errpkt(reason) CHAR *reason; { /* Send an error packet. */ | |
1200 | int x, y; | |
1201 | encstr(reason); | |
1202 | y = spack('E',pktnum,size,encbuf+7); | |
1203 | x = quiet; quiet = 1; /* Close files silently. */ | |
1204 | clsif(); clsof(1); | |
1205 | quiet = x; | |
1206 | #ifdef COMMENT | |
1207 | screen(SCR_TC,0,0l,""); | |
1208 | #endif /* COMMENT */ | |
1209 | if (what < W_CONNECT) | |
1210 | xitsta |= what; /* Remember what failed. */ | |
1211 | success = 0; | |
1212 | return(y); | |
1213 | } | |
1214 | ||
1215 | /* scmd() -- Send a packet of the given type */ | |
1216 | ||
1217 | int | |
1218 | #ifdef CK_ANSIC | |
1219 | scmd(char t, CHAR *dat) | |
1220 | #else | |
1221 | scmd(t,dat) char t; CHAR *dat; | |
1222 | #endif /* CK_ANSIC */ | |
1223 | /* scmd */ { | |
1224 | encstr(dat); /* Encode the command string */ | |
1225 | spack(t,pktnum,size,(CHAR *)(encbuf+7)); | |
1226 | return(0); | |
1227 | } | |
1228 | ||
1229 | VOID | |
1230 | srinit() { /* Send R (GET) packet */ | |
1231 | encstr((CHAR *)cmarg); /* Encode the filename. */ | |
1232 | spack('R',pktnum,size,encbuf+7); /* Send the packet. */ | |
1233 | } | |
1234 | \f | |
1235 | /* R P A C K -- Read a Packet */ | |
1236 | ||
1237 | /* | |
1238 | rpack reads a packet and returns the packet type, or else Q if the | |
1239 | packet was invalid, or T if a timeout occurred. Upon successful return, sets | |
1240 | the values of global rsn (received sequence number), rln (received | |
1241 | data length), and rdatap (pointer to null-terminated data field). | |
1242 | */ | |
1243 | int | |
1244 | rpack() { | |
1245 | register int i, j, x, lp; /* Local variables */ | |
1246 | int k, type, chklen; | |
1247 | unsigned crc; | |
1248 | CHAR pbc[4]; /* Packet block check */ | |
1249 | CHAR *sohp; /* Pointer to SOH */ | |
1250 | CHAR e; /* Packet end character */ | |
1251 | ||
1252 | debug(F101,"entering rpack, pktnum","",pktnum); | |
1253 | k = getrbuf(); /* Get a new packet input buffer. */ | |
1254 | debug(F101,"rpack getrbuf","",k); | |
1255 | if (k < 0) return(-1); /* Return like this if none free. */ | |
1256 | recpkt = r_pkt[k].bf_adr; | |
1257 | *recpkt = '\0'; /* Clear receive buffer. */ | |
1258 | sohp = recpkt; /* Initialize pointers to it. */ | |
1259 | rdatap = recpkt; | |
1260 | rsn = rln = -1; /* In case of failure. */ | |
1261 | e = (turn) ? turnch : eol; /* Use any handshake char for eol */ | |
1262 | ||
1263 | /* Try to get a "line". */ | |
1264 | ||
1265 | #ifdef PARSENSE | |
1266 | #ifdef UNIX | |
1267 | /* | |
1268 | So far the final turn argument is only for ck[uvd]tio.c. Should be added | |
1269 | to the others too. (turn == handshake character.) | |
1270 | */ | |
1271 | j = ttinl(recpkt,r_pkt[k].bf_len - 1,timint,e,stchr,turn); | |
1272 | #else | |
1273 | #ifdef VMS | |
1274 | j = ttinl(recpkt,r_pkt[k].bf_len - 1,timint,e,stchr,turn); | |
1275 | #else | |
1276 | #ifdef datageneral | |
1277 | j = ttinl(recpkt,r_pkt[k].bf_len - 1,timint,e,stchr,turn); | |
1278 | #else | |
1279 | j = ttinl(recpkt,r_pkt[k].bf_len - 1,timint,e,stchr); | |
1280 | #endif /* datageneral */ | |
1281 | #endif /* VMS */ | |
1282 | #endif /* UNIX */ | |
1283 | if (parity != ttprty) autopar = 1; | |
1284 | parity = ttprty; | |
1285 | #else | |
1286 | j = ttinl(recpkt,r_pkt[k].bf_len - 1,timint,e); | |
1287 | #endif /* PARSENSE */ | |
1288 | if (j < 0) { | |
1289 | debug(F101,"rpack: ttinl fails","",j); /* Otherwise, */ | |
1290 | freerbuf(k); /* Free this buffer */ | |
1291 | if (j < -1) { /* Bail out if ^C^C typed. */ | |
1292 | debug(F101,"rpack ^C server","",server); | |
1293 | debug(F101,"rpack ^C en_fin","",en_fin); | |
1294 | if (server == 0) return(j); /* But not if in server mode */ | |
1295 | else if (en_fin) return(j); /* with DISABLE FINISH */ | |
1296 | } | |
1297 | if (nakstate) /* call it a timeout. */ | |
1298 | screen(SCR_PT,'T',(long)winlo,""); | |
1299 | else | |
1300 | screen(SCR_PT,'T',(long)pktnum,""); | |
1301 | logpkt('r',-1,(CHAR *)"<timeout>"); | |
1302 | if (flow == 1) ttoc(XON); /* In case of Xoff blockage. */ | |
1303 | return('T'); | |
1304 | } | |
1305 | rpktl = j; | |
1306 | tlci += j; /* All OK, Count the characters. */ | |
1307 | flci += j; | |
1308 | ||
1309 | #ifndef PARSENSE | |
1310 | /* THEN eliminate this loop... */ | |
1311 | for (i = 0; (recpkt[i] != stchr) && (i < j); i++) | |
1312 | sohp++; /* Find mark */ | |
1313 | if (i++ >= j) { /* Didn't find it. */ | |
1314 | logpkt('r',-1,"<timeout>"); | |
1315 | freerbuf(k); | |
1316 | return('T'); | |
1317 | } | |
1318 | #else | |
1319 | i = 1; | |
1320 | #endif /* PARSENSE */ | |
1321 | ||
1322 | rpackets++; | |
1323 | lp = i; /* Remember LEN position. */ | |
1324 | if ((j = xunchar(recpkt[i++])) == 0) { | |
1325 | if ((j = lp+5) > MAXRP) return('Q'); /* Long packet */ | |
1326 | x = recpkt[j]; /* Header checksum. */ | |
1327 | recpkt[j] = '\0'; /* Calculate & compare. */ | |
1328 | if (xunchar(x) != chk1(recpkt+lp)) { | |
1329 | freerbuf(k); | |
1330 | logpkt('r',-1,(CHAR *)"<crunched:hdr>"); | |
1331 | return('Q'); | |
1332 | } | |
1333 | recpkt[j] = x; /* Checksum ok, put it back. */ | |
1334 | rln = xunchar(recpkt[j-2]) * 95 + xunchar(recpkt[j-1]) - bctl; | |
1335 | j = 3; /* Data offset. */ | |
1336 | } else if (j < 3) { | |
1337 | debug(F101,"rpack packet length less than 3","",j); | |
1338 | freerbuf(k); | |
1339 | logpkt('r',-1,(CHAR *)"<crunched:len>"); | |
1340 | return('Q'); | |
1341 | } else { | |
1342 | rln = j - bctl - 2; /* Regular packet */ | |
1343 | j = 0; /* No extended header */ | |
1344 | } | |
1345 | rsn = xunchar(recpkt[i++]); /* Sequence number */ | |
1346 | logpkt('r',rsn,sohp); | |
1347 | if (rsn < 0 || rsn > 63) { | |
1348 | debug(F101,"rpack bad sequence number","",rsn); | |
1349 | freerbuf(k); | |
1350 | logpkt('r',rsn,(CHAR *)"<crunched:seq>"); | |
1351 | return('Q'); | |
1352 | } | |
1353 | /* | |
1354 | If this packet has the same type as the packet just sent, assume it is | |
1355 | an echo and ignore it. Don't even bother with the block check calculation: | |
1356 | even if the packet is corrupted, we don't want to NAK an echoed packet. | |
1357 | (And we certainly don't want to NAK an ACK or NAK!) | |
1358 | */ | |
1359 | type = recpkt[i++]; /* Get packet's TYPE field */ | |
1360 | if (type == sndtyp || (nakstate && (type == 'N' /* || type == 'Y' */ ))) { | |
1361 | debug(F000,"rpack echo","",type); /* If it's an echo */ | |
1362 | freerbuf(k); /* Free this buffer */ | |
1363 | logpkt('#',rsn,(CHAR *)"<echo:ignored>"); | |
1364 | return('e'); /* return special (lowercase) code */ | |
1365 | } | |
1366 | /* | |
1367 | Separate the data from the block check, accounting for the case where | |
1368 | a packet was retransmitted after the block check switched. | |
1369 | */ | |
1370 | if (type == 'I' || type == 'S') { /* I & S packets always have type 1 */ | |
1371 | chklen = 1; | |
1372 | rln = rln + bctl - 1; | |
1373 | } else if (type == 'N') { /* A NAK packet never has data */ | |
1374 | chklen = xunchar(recpkt[lp]) - 2; | |
1375 | rln = rln + bctl - chklen; | |
1376 | } else chklen = bctl; | |
1377 | debug(F101,"rpack bctl","",bctl); | |
1378 | debug(F101,"rpack chklen","",chklen); | |
1379 | ||
1380 | i += j; /* Buffer index of DATA field */ | |
1381 | rdatap = recpkt+i; /* Pointer to DATA field */ | |
1382 | if ((j = rln + i) > r_pkt[k].bf_len ) { /* Make sure it fits */ | |
1383 | debug(F101,"packet sticks out too far","",j); | |
1384 | freerbuf(k); | |
1385 | logpkt('r',rsn,(CHAR *)"<overflow>"); | |
1386 | return('Q'); | |
1387 | } | |
1388 | ||
1389 | for (x = 0; x < chklen; x++) /* Copy the block check */ | |
1390 | pbc[x] = recpkt[j+x]; | |
1391 | pbc[x] = '\0'; /* Null-terminate block check string */ | |
1392 | recpkt[j] = '\0'; /* and the packet DATA field. */ | |
1393 | ||
1394 | if (chklen == 2 && bctu == 4) { /* Adjust for Blank-Free-2 */ | |
1395 | chklen = 4; /* (chklen is now a misnomer...) */ | |
1396 | debug(F100,"rpack block check B","",0); | |
1397 | } | |
1398 | switch (chklen) { /* Check the block check */ | |
1399 | case 1: /* Type 1, 6-bit checksum */ | |
1400 | if (xunchar(*pbc) != chk1(recpkt+lp)) { | |
1401 | debug(F110,"checked chars",recpkt+lp,0); | |
1402 | debug(F101,"block check","",(int) xunchar(*pbc)); | |
1403 | debug(F101,"should be","",chk1(recpkt+lp)); | |
1404 | freerbuf(k); | |
1405 | logpkt('r',-1,(CHAR *)"<crunched:chk1>"); | |
1406 | return('Q'); | |
1407 | } | |
1408 | break; | |
1409 | case 2: /* Type 2, 12-bit checksum */ | |
1410 | x = xunchar(*pbc) << 6 | xunchar(pbc[1]); | |
1411 | if (x != chk2(recpkt+lp)) { /* No match */ | |
1412 | if (type == 'E') { /* Allow E packets to have type 1 */ | |
1413 | recpkt[j++] = pbc[0]; | |
1414 | recpkt[j] = '\0'; | |
1415 | if (xunchar(pbc[1]) == chk1(recpkt+lp)) | |
1416 | break; | |
1417 | else | |
1418 | recpkt[--j] = '\0'; | |
1419 | } | |
1420 | debug(F110,"checked chars",recpkt+lp,0); | |
1421 | debug(F101,"block check","", x); | |
1422 | debug(F101,"should be","", (int) chk2(recpkt+lp)); | |
1423 | freerbuf(k); | |
1424 | logpkt('r',-1,(CHAR *)"<crunched:chk2>"); | |
1425 | return('Q'); | |
1426 | } | |
1427 | break; | |
1428 | case 3: /* Type 3, 16-bit CRC */ | |
1429 | crc = (xunchar(pbc[0]) << 12) | |
1430 | | (xunchar(pbc[1]) << 6) | |
1431 | | (xunchar(pbc[2])); | |
1432 | if (crc != chk3(recpkt+lp)) { | |
1433 | if (type == 'E') { /* Allow E packets to have type 1 */ | |
1434 | recpkt[j++] = pbc[0]; | |
1435 | recpkt[j++] = pbc[1]; | |
1436 | recpkt[j] = '\0'; | |
1437 | if (xunchar(pbc[2]) == chk1(recpkt+lp)) | |
1438 | break; | |
1439 | else { j -=2; recpkt[j] = '\0'; } | |
1440 | } | |
1441 | debug(F110,"checked chars",recpkt+lp,0); | |
1442 | debug(F101,"block check","",xunchar(*pbc)); | |
1443 | debug(F101,"should be","",(int) chk3(recpkt+lp)); | |
1444 | freerbuf(k); | |
1445 | logpkt('r',-1,(CHAR *)"<crunched:chk3>"); | |
1446 | return('Q'); | |
1447 | } | |
1448 | break; | |
1449 | case 4: /* Type 4 = Type 2, no blanks. */ | |
1450 | x = (unsigned)((xunchar(*pbc) - 1) << 6) | | |
1451 | (unsigned)(xunchar(pbc[1]) - 1); | |
1452 | if (x != chk2(recpkt+lp)) { | |
1453 | if (type == 'E') { /* Allow E packets to have type 1 */ | |
1454 | recpkt[j++] = pbc[0]; | |
1455 | recpkt[j] = '\0'; | |
1456 | if (xunchar(pbc[1]) == chk1(recpkt+lp)) | |
1457 | break; | |
1458 | else | |
1459 | recpkt[--j] = '\0'; | |
1460 | } | |
1461 | debug(F101,"bad type B block check","",x); | |
1462 | freerbuf(k); | |
1463 | logpkt('r',-1,(CHAR *)"<crunched:chkb>"); | |
1464 | return('Q'); | |
1465 | } | |
1466 | break; | |
1467 | default: /* Shouldn't happen... */ | |
1468 | freerbuf(k); | |
1469 | logpkt('r',-1,(CHAR *)"<crunched:chkx>"); | |
1470 | return('Q'); | |
1471 | } | |
1472 | debug(F101,"rpack block check OK","",rsn); | |
1473 | ||
1474 | /* Now we can believe the sequence number, and other fields. */ | |
1475 | /* Here we violate strict principles of layering, etc, and look at the */ | |
1476 | /* packet sequence number. If there's already a packet with the same */ | |
1477 | /* number in the window, we remove this one so that the window will not */ | |
1478 | /* fill up. */ | |
1479 | ||
1480 | if ((x = rseqtbl[rsn]) != -1) { /* Already a packet with this number */ | |
1481 | retrans++; /* Count it for statistics */ | |
1482 | debug(F101,"rpack got dup","",rsn); | |
1483 | logpkt('r',rsn,(CHAR *)"<duplicate>"); | |
1484 | freerbuf(x); /* Free old buffer, keep new packet. */ | |
1485 | r_pkt[k].pk_rtr++; /* Count this as a retransmission. */ | |
1486 | } | |
1487 | ||
1488 | /* New packet, not seen before, enter it into the receive window. */ | |
1489 | ||
1490 | rseqtbl[rsn] = k; /* Make back pointer */ | |
1491 | r_pkt[k].pk_seq = rsn; /* Record in packet info structure */ | |
1492 | r_pkt[k].pk_typ = type; /* Sequence, type,... */ | |
1493 | r_pkt[k].pk_adr = rdatap; /* pointer to data buffer */ | |
1494 | screen(SCR_PT,(char)type,(long)rsn,(char *)sohp); /* Update screen */ | |
1495 | return(type); /* Return packet type */ | |
1496 | } | |
1497 | ||
1498 | /* L O G P K T -- Log packet number n, pointed to by s. */ | |
1499 | ||
1500 | /* c = 's' (send) or 'r' (receive) */ | |
1501 | ||
1502 | VOID | |
1503 | #ifdef CK_ANSIC | |
1504 | logpkt(char c,int n, CHAR *s) | |
1505 | #else | |
1506 | logpkt(c,n,s) char c; int n; CHAR *s; | |
1507 | #endif /* CK_ANSIC */ | |
1508 | /* logpkt */ { | |
1509 | char plog[20]; | |
1510 | if (pktlog && *s) { | |
1511 | if (n < 0) | |
1512 | sprintf(plog,"%c-xx-%02d-",c,(gtimer()%60)); | |
1513 | else | |
1514 | sprintf(plog,"%c-%02d-%02d-",c,n,(gtimer()%60)); | |
1515 | if (zsout(ZPFILE,plog) < 0) pktlog = 0; | |
1516 | else if (zsoutl(ZPFILE,(char *)s) < 0) pktlog = 0; | |
1517 | } | |
1518 | } | |
1519 | ||
1520 | #ifdef TLOG | |
1521 | ||
1522 | /* T S T A T S -- Record statistics in transaction log */ | |
1523 | ||
1524 | VOID | |
1525 | tstats() { | |
1526 | char *tp; | |
1527 | ztime(&tp); /* Get time stamp */ | |
1528 | tlog(F110,"End of transaction",tp,0L); /* Record it */ | |
1529 | ||
1530 | if (filcnt < 1) return; /* If no files, done. */ | |
1531 | ||
1532 | /* If multiple files, record character totals for all files */ | |
1533 | ||
1534 | if (filcnt > 1) { | |
1535 | tlog(F101," files","",filcnt); | |
1536 | tlog(F101," total file characters ","",tfc); | |
1537 | tlog(F101," communication line in ","",tlci); | |
1538 | tlog(F101," communication line out ","",tlco); | |
1539 | } | |
1540 | ||
1541 | /* Record timing info for one or more files */ | |
1542 | ||
1543 | tlog(F101," elapsed time (seconds) ","",(long) tsecs); | |
1544 | if (tsecs > 0) { | |
1545 | long lx; | |
1546 | lx = (tfc * 10L) / (long) tsecs; | |
1547 | tlog(F101," effective data rate ","",lx/10L); | |
1548 | if (speed <= 0L) speed = ttgspd(); | |
1549 | if (speed > 0L && speed != 8880L && network == 0) { | |
1550 | lx = (lx * 100L) / speed; | |
1551 | tlog(F101," efficiency (percent) ","",lx); | |
1552 | } | |
1553 | } | |
1554 | tlog(F100,"","",0L); /* Leave a blank line */ | |
1555 | } | |
1556 | ||
1557 | /* F S T A T S -- Record file statistics in transaction log */ | |
1558 | ||
1559 | VOID | |
1560 | fstats() { | |
1561 | tfc += ffc; | |
1562 | tlog(F100," end of file","",0L); | |
1563 | tlog(F101," file characters ","",ffc); | |
1564 | tlog(F101," communication line in ","",flci); | |
1565 | tlog(F101," communication line out ","",flco); | |
1566 | } | |
1567 | #else /* NOTLOG */ | |
1568 | VOID | |
1569 | tstats() {} | |
1570 | ||
1571 | VOID | |
1572 | fstats() { | |
1573 | tfc += ffc; | |
1574 | } | |
1575 | #endif /* TLOG */ |