1 /* C K C F N 3 -- Packet buffer management for C-Kermit */
3 /* (plus assorted functions tacked on at the end) */
6 Author: Frank da Cruz <fdc@columbia.edu>,
7 Columbia University Academic Information Systems, New York City.
9 Copyright (C) 1985, 2010,
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.
25 /* C K M K D I R -- Create a directory */
28 int fc = 0 to create, nonzero to remove, a directory.
29 char * s = pointer to name of directory to create or remove.
30 char ** r = address of pointer to return name or message.
31 int m = 1 to print error messages, 0 to be silent.
32 int cvt = 1 means convert s from standard format to local format;
35 0 on success (directory was created or removed).
36 -1 when attempt to create the directory failed.
37 -2 on internal error (e.g. no code for creating directories).
38 On success, the name is pointed to by p.
39 On failure, the reason is pointed to by p.
42 static char ckmkdbuf[CKMAXPATH+1];
45 static char ckmkdbuf[CKMAXPATH+1];
46 #endif /* datageneral */
51 ckmkdir(fc,s,r,m,cvt) int fc; char * s; char ** r; int m; int cvt; {
53 char tmpbuf[CKMAXPATH+1];
54 char buf2[CKMAXPATH+1];
56 debug(F110,"ckmkdir 1 fc",s,fc);
60 (fc == 0) ? "mkdir" : "rmdir",
69 /* Come back and make this nicer later if anybody notices */
70 if (fc == 0) { /* mkdir */
73 /* AOS/VS rmdir() is a no-op. */
74 ckmakmsg(tmpbuf,CKMAXPATH+1,"delete ",s,NULL,NULL);
75 debug(F110,"ckmkdir 2",tmpbuf,0);
79 #else /* not datageneral */
81 /* First make sure the name has an acceptable directory-name format */
86 int lb = 0, rb = 0, sl = 0;
88 if (*p == '[' || *p == '<') lb++; /* Count brackets */
89 else if (*p == ']' || *p == '>') rb++;
90 else if (*p == '/') sl++; /* and slashes */
93 if (lb != 1 && rb != 1 && sl == 0 && p > s && *(p-1) != ':') {
94 /* Probably just a word - convert to VMS format */
98 (*s == '.') ? "" : ".",
103 } else if (lb == 0 && rb == 0 && sl != 0 && p > s && *(p-1) != ':') {
105 /* Seems to be in UNIX format */
107 if (x > 0 && s[x-1] != '/')
109 ckmakmsg(buf2,CKMAXPATH+1,s,flag ? "/" : "",NULL,NULL);
113 ckstrncpy(tmpbuf,s,CKMAXPATH+1);
116 debug(F110,"ckmkdir 2+VMS",s,0);
123 ckstrncpy(tmpbuf,s,CKMAXPATH+1);
126 if (x > 0 && s[x-1] != '/') { /* Must end in "/" for zmkdir() */
129 debug(F110,"ckmkdir 2+UNIXOROSK",s,0);
131 #else /* UNIXOROSK */
133 ckstrncpy(tmpbuf,s,CKMAXPATH+1);
136 if (fc == 0 && x > 0 && s[x-1] != '/') { /* Must end in "/" for zmkdir() */
139 debug(F110,"ckmkdir 2+OS2",s,0);
142 #endif /* UNIXOROSK */
145 /* Server is calling us, so convert to local format if necessary */
147 nzrtol(s,(char *)buf2,1,PATH_ABS,CKMAXPATH);
149 debug(F110,"ckmkdir 3",s,0);
152 debug(F110,"ckmkdir 4",s,0);
153 if (fc == 0) { /* Making */
162 #endif /* CK_MKDIR */
163 } else { /* Removing */
178 #endif /* datageneral */
179 debug(F101,"ckmkdir rc","",rc);
184 (fc == 0) ? "creation" : "removal",
185 "not implemented in this version of C-Kermit",
189 if (m) printf("%s\n",*r);
192 ckmakmsg(ckmkdbuf,CKMAXPATH,s,": ",ck_errstr(),NULL);
194 } else if (fc == 0 && zfnqfp(s,CKMAXPATH,ckmkdbuf)) {
196 } else if (fc != 0) {
197 ckmakmsg(ckmkdbuf,CKMAXPATH,s,": removed",NULL,NULL);
202 #endif /* CK_MKDIR */
204 #ifndef NOXFER /* Rest of this file... */
214 extern char ** sndfilter;
215 #endif /* PIPESEND */
217 extern int unkcs, wmax, wcur, discard, bctu, bctl, local, fdispla, what,
218 sendmode, opnerr, dest, epktrcvd, epktsent, filestatus, eofmethod, dispos,
222 extern char * rfspec, * sfspec, * prfspec, * psfspec, * rrfspec, * prrfspec;
225 extern char * ofperms;
233 extern int xflg, remfile, remappd;
235 extern char filnam[];
237 extern int rprintf, rmailf; /* REMOTE MAIL, PRINT */
238 char optbuf[OPTBUFLEN]; /* Options for MAIL or REMOTE PRINT */
239 #endif /* NOFRILLS */
241 extern int fblksiz, frecl, forg, frecfm, fncact, fncsav, fcctrl, lf_opts;
242 extern CHAR * srvcmd;
243 extern int srvcmdlen;
245 extern int binary, spsiz;
246 extern int pktnum, cxseen, czseen, nfils, stdinf;
247 extern int memstr, stdouf, keep, sndsrc, hcflg;
248 extern int server, en_cwd, en_mai, en_pri;
250 /* Attributes in/out enabled flags */
253 atenci, atenco, atdati, atdato, atleni, atleno, atblki, atblko,
254 attypi, attypo, atsidi, atsido, atsysi, atsyso, atdisi, atdiso;
257 extern int atlpri, atlpro, atgpri, atgpro;
258 #endif /* CK_PERMS */
261 extern int atfrmi, atfrmo, atcrei, atcreo, atacti, atacto;
266 #endif /* datageneral */
269 extern CK_OFF_T fsize, ffc, tfc, sendstart, calibrate;
273 _PROTOTYP (VOID setxlate, (void));
274 extern int tcharset, fcharset;
275 extern int ntcsets, xlatype, xfrxla;
276 extern struct csinfo tcsinfo[], fcsinfo[];
279 /* Variables global to Kermit that are defined in this module */
281 #ifdef CKXXCHAR /* DOUBLE / IGNORE char table */
285 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
286 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
287 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
288 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
289 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
290 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
291 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
292 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
294 #endif /* CKXXCHAR */
296 int winlo; /* packet number at low window edge */
298 int sbufnum; /* number of free buffers */
299 int dum001 = 1234; /* protection... */
300 int sbufuse[MAXWS]; /* buffer in-use flag */
302 int rbufnum; /* number of free buffers */
303 int dum002 = 4321; /* more protection */
304 int rbufuse[MAXWS]; /* buffer in-use flag */
305 int sseqtbl[64]; /* sequence # to buffer # table */
306 int rseqtbl[64]; /* sequence # to buffer # table */
307 int sacktbl[64]; /* sequence # ack table */
309 int o_isopen = 0, i_isopen = 0; /* Input & output files are open */
312 struct pktinfo *s_pkt = NULL; /* array of pktinfo structures */
313 struct pktinfo *r_pkt = NULL; /* array of pktinfo structures */
315 struct pktinfo s_pkt[MAXWS]; /* array of pktinfo structures */
316 struct pktinfo r_pkt[MAXWS]; /* array of pktinfo structures */
320 char xbuf[200]; /* For debug logging */
324 CHAR *bigsbuf = NULL, *bigrbuf = NULL;
326 char bigsbt[8]; /* Protection (shouldn't need this). */
327 /* BUT DON'T REMOVE IT! */
328 CHAR bigsbuf[SBSIZ + 5]; /* Send-packet buffer area */
329 char bigrbt[8]; /* Safety padding */
330 CHAR bigrbuf[RBSIZ + 5]; /* Receive-packet area */
332 int bigsbsiz = SBSIZ; /* Sizes of big send & rcv buffers. */
333 int bigrbsiz = RBSIZ;
336 int zchkpath(char *s);
343 long maxbufsiz = RBSIZ; /* Configuration parameters */
344 int maxpktsiz = MAXSP;
345 extern int spsizf, /* For bug in IRIX Telnet server */
346 rpsiz, urpsiz, spsizr, spmax, wslotr;
347 extern struct ck_p ptab[];
349 if (maxpktsiz < 40) /* Long packet length */
351 else if (maxpktsiz > 4000)
353 wslotr = maxbufsiz / maxpktsiz;
354 if (wslotr > MAXWS) /* Window slots */
360 urpsiz = adjpkl(maxpktsiz,wslotr,maxbufsiz);
361 ptab[PROTO_K].rpktlen = urpsiz;
362 rpsiz = (urpsiz > 94) ? 94 : urpsiz; /* Max non-long packet length */
363 debug(F111,"dofast","uprsiz",urpsiz);
366 /* IRIX Telnet server chops off writes longer than 4K */
367 spsiz = spmax = spsizr = urpsiz;
368 debug(F101,"doarg Q IRIX spsiz","",spsiz);
373 setprefix(PX_CAU); /* Cautious unprefixing */
374 #endif /* CK_SPEED */
378 /* For sanity, use "i" for buffer slots, "n" for packet numbers. */
383 Allocates the big send and receive buffers.
384 Call with size for big send buffer (s) and receive buffer (r).
385 These sizes can be different.
386 Attempts to allocate buffers of the requested size, but if it can't,
387 it will allocate smaller ones.
388 Sets global variables bigsbsiz and bigrbsiz to the actual sizes,
389 and bigsbuf and bigrbuf pointing to the actual buffers.
390 Designed to be called more than once.
391 Returns 0 on success, -1 on failure.
394 CHAR *bigbufp = NULL;
397 inibufs(s,r) int s, r; {
402 unsigned /* Don't you wish everybody had unsigned long... */
407 debug(F101,"inibufs s","",s);
408 debug(F101,"inibufs r","",r);
410 if (s < 80 || r < 80) return(-1); /* Validate arguments. */
412 if (!s_pkt) { /* Allocate packet info structures */
413 if (!(s_pkt = (struct pktinfo *) malloc(sizeof(struct pktinfo)*MAXWS)))
414 fatal("ini_pkts: no memory for s_pkt");
416 for (x = 0; x < MAXWS; x++)
417 s_pkt[x].pk_adr = NULL; /* Initialize addresses */
420 if (!(r_pkt = (struct pktinfo *) malloc(sizeof(struct pktinfo)*MAXWS)))
421 fatal("ini_pkts: no memory for s_pkt");
423 for (x = 0; x < MAXWS; x++)
424 r_pkt[x].pk_adr = NULL; /* Initialize addresses */
426 if (!srvcmd) { /* Allocate srvcmd buffer */
427 srvcmd = (CHAR *) malloc(r + 100);
428 if (!srvcmd) return(-1);
432 if (bigbufp) { /* Free previous buffers, if any. */
436 size = s + r + 40; /* Combined requested size + padding */
437 z = (unsigned) s + (unsigned) r + 40;
438 debug(F101,"inibufs size 1","",size);
439 debug(F101,"inibufs size z","",z);
440 if ((long) size != z) {
441 debug(F100,"inibufs overflow","",0);
445 /* Try to get the space. If malloc fails, try to get a little less. */
446 /* (Obviously, this algorithm can be refined.) */
448 while (!(bigbufp = (CHAR *) malloc(size))) {
449 debug(F101,"inibufs bigbuf malloc failed","",size);
450 size = (size * 2) / 3; /* Failed, cut size by 1/3. */
451 if (size < 200) /* Try again until too small. */
454 debug(F101,"inibufs size 2","",size); /* OK, we got some space. */
457 Now divide the allocated space between the send and receive buffers in the
458 requested proportion. The natural formula would be (s / (s + r)) * size
459 (for the send buffer), but that doesn't work with integer arithmetic and we
460 can't use floating point because some machines don't have it. This can be
461 rearranged as (s * size) / (s + r). But (s * size) can be VERY large, too
462 large for 32 bits. So let's do it this way. This arithmetic works for
463 buffer sizes up to about 5,000,000.
466 z = ( (long) s * FACTOR ) / ( (long) s + (long) r );
467 x = ( z * ( (long) size / FACTOR ) );
468 if (x < 0) return(-1); /* Catch overflow */
470 bigsbsiz = x - 5; /* Size of send buffer */
471 bigsbuf = bigbufp; /* Address of send buffer */
472 debug(F101,"inibufs bigsbsiz","",bigsbsiz);
474 bigrbsiz = size - x - 5; /* Size of receive buffer */
475 bigrbuf = bigbufp + x; /* Addresss of receive buffer */
476 debug(F101,"inibufs bigrbsiz","",bigrbsiz);
478 return(0); /* Success */
479 #else /* No dynamic allocation */
480 bigsbsiz = SBSIZ; /* Just use the symbols */
481 bigrbsiz = RBSIZ; /* ... */
482 return(0); /* Success. */
487 /* M A K E B U F -- Makes and clears a new buffers. */
490 /* slots: number of buffer slots to make, 1 to 32 */
491 /* bufsiz: size of the big buffer */
492 /* buf: address of the big buffer */
493 /* xx: pointer to array of pktinfo structures for these buffers */
495 /* Subdivides the big buffer into "slots" buffers. */
498 /* -1 if too many or too few slots requested, */
499 /* -2 if slots would be too small. */
500 /* n (positive) on success = size of one buffer. */
501 /* with pktinfo structure initialized for this set of buffers. */
504 makebuf(slots,bufsiz,buf,xx)
505 /* makebuf */ int slots, bufsiz; CHAR buf[]; struct pktinfo *xx; {
510 debug(F101,"makebuf","",slots);
511 debug(F101,"makebuf bufsiz","",bufsiz);
512 debug(F101,"makebuf MAXWS","",MAXWS);
514 if (slots > MAXWS || slots < 1) return(-1);
515 if (bufsiz < slots * 10 ) return(-2);
517 size = bufsiz / slots; /* Divide up the big buffer. */
518 a = buf; /* Address of first piece. */
520 for (i = 0; i < slots; i++) {
521 struct pktinfo *x = &xx[i];
522 x->bf_adr = a; /* Address of this buffer */
523 x->bf_len = size; /* Length of this buffer */
524 x->pk_len = 0; /* Length of data field */
525 x->pk_typ = ' '; /* packet type */
526 x->pk_seq = -1; /* packet sequence number */
527 x->pk_rtr = 0; /* retransmissions */
528 *a = '\0'; /* Clear the buffer */
529 a += size; /* Position to next buffer slot */
534 /* M A K S B U F -- Makes the send-packet buffer */
537 mksbuf(slots) int slots; {
540 if ((x = makebuf(slots,bigsbsiz,bigsbuf,s_pkt)) < 0) {
541 debug(F101,"mksbuf makebuf return","",x);
544 debug(F101,"mksbuf makebuf return","",x);
545 for (i = 0; i < 64; i++) { /* Initialize sequence-number- */
546 sseqtbl[i] = -1; /* to-buffer-number table. */
549 for (i = 0; i < MAXWS; i++)
550 sbufuse[i] = 0; /* Mark each buffer as free */
556 /* M A K R B U F -- Makes the receive-packet buffer */
559 mkrbuf(slots) int slots; {
562 if ((x = makebuf(slots,bigrbsiz,bigrbuf,r_pkt)) < 0) {
563 debug(F101,"mkrbuf makebuf return","",x);
566 debug(F101,"mkrbuf makebuf return","",x);
567 for (i = 0; i < 64; i++) { /* Initialize sequence-number- */
568 rseqtbl[i] = -1; /* to-buffer-number table. */
570 for (i = 0; i < MAXWS; i++)
571 rbufuse[i] = 0; /* Mark each buffer as free */
577 /* W I N D O W -- Resize the window to n */
581 debug(F101,"window","",n);
582 if (n < 1 || n > MAXWS) return(-1);
583 if (mksbuf(n) < 0) return(-1);
584 if (mkrbuf(n) < 0) return(-1);
587 if (deblog) dumpsbuf();
588 if (deblog) dumprbuf();
593 /* G E T S B U F -- Allocate a send-buffer. */
595 /* Call with packet sequence number to allocate buffer for. */
597 /* -4 if argument is invalid (negative, or greater than 63) */
598 /* -3 if buffers were thought to be available but really weren't (bug!) */
599 /* -2 if the number of free buffers is negative (bug!) */
600 /* -1 if no free buffers. */
601 /* 0 or positive, packet sequence number, with buffer allocated for it. */
604 getsbuf(n) int n; { /* Allocate a send-buffer */
607 if (n < 0 || n > 63) {
608 debug(F101,"getsbuf bad arg","",n);
609 return(-4); /* Bad argument */
611 debug(F101,"getsbuf packet","",n);
612 /* debug(F101,"getsbuf, sbufnum","",sbufnum); */
613 if (sbufnum == 0) return(-1); /* No free buffers. */
614 if (sbufnum < 0) return(-2); /* Shouldn't happen. */
615 for (i = 0; i < wslots; i++) /* Find the first one not in use. */
616 if (sbufuse[i] == 0) { /* Got one? */
617 sbufuse[i] = 1; /* Mark it as in use. */
618 sbufnum--; /* One less free buffer. */
619 *s_pkt[i].bf_adr = '\0'; /* Zero the buffer data field */
620 s_pkt[i].pk_seq = n; /* Put in the sequence number */
621 sseqtbl[n] = i; /* Back pointer from sequence number */
622 sacktbl[n] = 0; /* ACK flag */
623 s_pkt[i].pk_len = 0; /* Data field length now zero. */
624 s_pkt[i].pk_typ = ' '; /* Blank the packet type too. */
625 s_pkt[i].pk_rtr = 0; /* Zero the retransmission count */
626 p = s_pkt[i].bf_adr + 7; /* Set global "data" address. */
627 debug(F101,"getsbuf p","",0);
630 debug(F100,"getsbuf data == NULL","",0);
633 if ((what & (W_SEND|W_REMO)) && (++wcur > wmax))
634 wmax = wcur; /* For statistics. */
635 /* debug(F101,"getsbuf wcur","",wcur); */
636 return(n); /* Return its index. */
638 sbufnum = 0; /* Didn't find one. */
639 return(-3); /* Shouldn't happen! */
643 getrbuf() { /* Allocate a receive buffer */
646 /* This code is pretty stable by now... */
647 /* Looks like we might need this after all */
648 debug(F101,"getrbuf rbufnum","",rbufnum);
649 debug(F101,"getrbuf wslots","",wslots);
650 debug(F101,"getrbuf dum002","",dum002);
651 debug(F101,"getrbuf dum003","",dum003);
653 if (rbufnum == 0) return(-1); /* No free buffers. */
654 if (rbufnum < 0) return(-2); /* Shouldn't happen. */
655 for (i = 0; i < wslots; i++) /* Find the first one not in use. */
656 if (rbufuse[i] == 0) { /* Got one? */
657 rbufuse[i] = 1; /* Mark it as in use. */
658 *r_pkt[i].bf_adr = '\0'; /* Zero the buffer data field */
659 rbufnum--; /* One less free buffer. */
660 debug(F101,"getrbuf new rbufnum","",rbufnum);
661 if ((what & W_RECV) && (++wcur > wmax))
662 wmax = wcur; /* For statistics. */
663 /* debug(F101,"getrbuf wcur","",wcur); */
664 return(i); /* Return its index. */
666 /* debug(F101,"getrbuf foulup","",i); */
667 rbufnum = 0; /* Didn't find one. */
668 return(-3); /* Shouldn't happen! */
671 /* F R E E S B U F -- Free send-buffer for given packet sequence number */
675 /* -1 if specified buffer does not exist */
678 freesbuf(n) int n; { /* Release send-buffer for packet n. */
681 debug(F101,"freesbuf","",n);
682 if (n < 0 || n > 63) /* No such packet. */
684 i = sseqtbl[n]; /* Get the window slot number. */
685 if (i > -1 && i <= wslots) {
686 sseqtbl[n] = -1; /* If valid, remove from seqtbl */
687 sbufnum++; /* and count one more free buffer */
688 sbufuse[i] = 0; /* and mark it as free, */
689 if (what & (W_SEND|W_REMO)) /* decrement active slots */
690 wcur--; /* for statistics and display. */
692 debug(F101," sseqtbl[n]","",sseqtbl[n]);
696 /* The following is done only so dumped buffers will look right. */
699 *s_pkt[i].bf_adr = '\0'; /* Zero the buffer data field */
700 s_pkt[i].pk_seq = -1; /* Invalidate the sequence number */
701 s_pkt[i].pk_len = 0; /* Data field length now zero. */
702 s_pkt[i].pk_typ = ' '; /* Blank the packet type too. */
703 s_pkt[i].pk_rtr = 0; /* And the retries field. */
709 freerbuf(i) int i; { /* Release receive-buffer slot "i". */
712 /* NOTE !! Currently, this function frees the indicated buffer, but */
713 /* does NOT erase the data. The program counts on this. Will find a */
714 /* better way later.... */
716 /* debug(F101,"freerbuf, slot","",i); */
717 if (i < 0 || i >= wslots) { /* No such slot. */
718 debug(F101,"freerbuf no such slot","",i);
721 n = r_pkt[i].pk_seq; /* Get the packet sequence number */
722 debug(F101,"freerbuf packet","",n);
723 if (n > -1 && n < 64) /* If valid, remove from seqtbl */
725 if (rbufuse[i] != 0) { /* If really allocated, */
726 rbufuse[i] = 0; /* mark it as free, */
727 rbufnum++; /* and count one more free buffer. */
728 if (what & W_RECV) /* Keep track of current slots */
729 wcur--; /* for statistics and display */
730 debug(F101,"freerbuf rbufnum","",rbufnum);
733 /* The following is done only so dumped buffers will look right. */
736 /* *r_pkt[i].bf_adr = '\0'; */ /* Zero the buffer data field */
737 r_pkt[i].pk_seq = -1; /* And from packet list */
738 r_pkt[i].pk_len = 0; /* Data field length now zero. */
739 r_pkt[i].pk_typ = ' '; /* Blank the packet type too. */
740 r_pkt[i].pk_rtr = 0; /* And the retries field. */
745 /* This is like freerbuf, except it's called with a packet sequence number */
746 /* rather than a packet buffer index. */
749 freerpkt(seq) int seq; {
751 debug(F101,"freerpkt seq","",seq);
753 /* debug(F101,"freerpkt k","",k); */
756 /* debug(F101,"freerpkt freerbuf","",k); */
761 /* C H K W I N -- Check if packet n is in window. */
764 /* 0 if it is in the current window, */
765 /* +1 if it would have been in previous window (e.g. if ack was lost), */
766 /* -1 if it is outside any window (protocol error), */
767 /* -2 if either of the argument packet numbers is out of range. */
769 /* Call with packet number to check (n), lowest packet number in window */
770 /* (bottom), and number of slots in window (slots). */
773 chkwin(n,bottom,slots) int n, bottom, slots; {
776 debug(F101,"chkwin packet","",n);
777 debug(F101,"chkwin winlo","",bottom);
778 debug(F101,"chkwin slots","",slots);
780 /* First do the easy and common cases, where the windows are not split. */
782 if (n < 0 || n > 63 || bottom < 0 || bottom > 63)
785 if (n == bottom) return(0); /* In a perfect world... */
787 top = bottom + slots; /* Calculate window top. */
788 if (top < 64 && n < top && n >= bottom)
789 return(0); /* In current window. */
791 prev = bottom - slots; /* Bottom of previous window. */
792 if (prev > -1 && n < bottom && n > prev)
793 return(1); /* In previous. */
795 /* Now consider the case where the current window is split. */
797 if (top > 63) { /* Wraparound... */
798 top -= 64; /* Get modulo-64 sequence number */
799 if (n < top || n >= bottom) {
800 return(0); /* In current window. */
801 } else { /* Not in current window. */
802 if (n < bottom && n >= prev) /* Previous window can't be split. */
803 return(1); /* In previous window. */
805 return(-1); /* Not in previous window. */
809 /* Now the case where current window not split, but previous window is. */
811 if (prev < 0) { /* Is previous window split? */
812 prev += 64; /* Yes. */
813 if (n < bottom || n >= prev)
814 return(1); /* In previous window. */
815 } else { /* Previous window not split. */
816 if (n < bottom && n >= prev)
817 return(1); /* In previous window. */
820 /* It's not in the current window, and not in the previous window... */
822 return(-1); /* So it's not in any window. */
826 dumpsbuf() { /* Dump send-buffers */
828 int j, x, z; /* to debug log. */
830 if (! deblog) return(0);
831 x = zsoutl(ZDFILE,"SEND BUFFERS:");
836 x=zsoutl(ZDFILE,"buffer inuse address length data type seq flag retries");
841 for (j = 0; j < wslots; j++) {
844 z = ((unsigned long)(s_pkt[j].bf_adr)) & 0xffff;
846 sprintf(xbuf, /* safe (200) */
847 "%4d%6d%10d%5d%6d%4c%5d%6d\n",
850 /* Avoid warnings when addresses are bigger than ints */
858 if (zsout(ZDFILE,xbuf) < 0) {
862 if (s_pkt[j].pk_adr) {
863 x = (int)strlen((char *) s_pkt[j].pk_adr);
865 sprintf(xbuf, /* safe (checked) */
866 "[%.72s%s]\n",s_pkt[j].pk_adr, x > 72 ? "..." : "");
868 sprintf(xbuf,"[(empty string)]\n"); /* safe (200) */
870 sprintf(xbuf,"[(null pointer)]\n"); /* safe (200) */
872 if (zsout(ZDFILE,xbuf) < 0) {
877 sprintf(xbuf,"free: %d, winlo: %d\n", sbufnum, winlo); /* safe (200) */
878 if (zsout(ZDFILE,xbuf) < 0) {
886 dumprbuf() { /* Dump receive-buffers */
889 if (! deblog) return(0);
890 if (zsoutl(ZDFILE,"RECEIVE BUFFERS:") < 0) {
894 x=zsoutl(ZDFILE,"buffer inuse address length data type seq flag retries");
899 for ( j = 0; j < wslots; j++ ) {
902 z = ((unsigned long)(r_pkt[j].bf_adr)) & 0xffff;
903 sprintf(xbuf, /* 200, safe */
904 "%4d%6d%10d%5d%6d%4c%5d%6d\n",
907 /* Avoid warnings when addresses are bigger than ints */
915 if (zsout(ZDFILE,xbuf) < 0) {
919 x = (int)strlen((char *)r_pkt[j].bf_adr);
920 sprintf(xbuf, /* safe (checked) */
921 "[%.72s%s]\n",r_pkt[j].bf_adr, x > 72 ? "..." : "");
922 if (zsout(ZDFILE,xbuf) < 0) {
927 sprintf(xbuf,"free: %d, winlo: %d\n", rbufnum, winlo); /* safe (200) */
928 if (zsout(ZDFILE,xbuf) < 0) {
936 /* S A T T R -- Send an Attribute Packet */
939 Sends attribute packet(s) for the current file. If the info will not
940 fit into one packet, it can be called repeatedly until all the fields
941 that will fit are sent.
944 xp == 0 if we're sending a real file (F packet), or:
945 xp != 0 for screen data (X packet).
947 flag == 1 for first A packet
948 flag == 0 for subsequent A packets.
950 1 or greater if an A packet was sent, or:
951 0 if an S-packet was not sent because there was no data to send or
952 there was no data left that was short enough to send, or:
953 -1 on any kind of error.
956 /* (don't) #define TSOFORMAT */
957 /* which was only for making C-Kermit send TSO-Kermit-like A packets */
958 /* to try to track down a problem somebody reported... */
961 sattr(xp, flag) int xp, flag; { /* Send Attributes */
963 static int max; /* Maximum length for Attributes */
964 static short done[95]; /* Field-complete array */
965 static struct zattr x; /* File attribute struct */
966 static char xdate[24];
968 extern char * cksysid;
970 /* Some extra flags are used because the "done" array is sparse */
972 int i, j, rc, aln, left = 0, numset = 0, xbin = 0; /* Workers */
976 notafile = sndarray || pipesend ||
979 #endif /* PIPESEND */
982 debug(F101,"sattr flag","",flag);
983 if (!flag) /* No more attributes to send */
984 if (done[xunchar('@')])
987 /* Initialize Attribute mechanism */
989 if (flag) { /* First time here for this file? */
990 initattr(&x); /* Blank out all the fields. */
991 for (j = 0; j < 95; j++) /* Init array of completed fields */
993 max = maxdata(); /* Get maximum data field length */
994 if (notafile || xp == 1) { /* Is it not a real file? */
995 extern char * zzndate();
999 if (calibrate) { /* Calibration run... */
1000 x.lengthk = calibrate / 1024L; /* We know the length */
1001 x.length = calibrate;
1003 #endif /* CALIBRATE */
1004 x.systemid.val = cksysid; /* System ID */
1005 x.systemid.len = (int)strlen(cksysid);
1006 ckstrncpy(xdate,zzndate(),24);
1009 for (i = 11; i < 19; i++) /* copy hh:mm:ss */
1010 xdate[i - 2] = p[i]; /* to xdate */
1011 xdate[17] = NUL; /* terminate */
1014 debug(F111,"sattr notafile date",x.date.val,x.date.len);
1015 } else { /* Real file */
1016 rc = zsattr(&x); /* Get attributes for this file */
1017 debug(F101,"sattr zsattr","",rc);
1018 if (rc < 0) /* Can't get 'em so don't send 'em */
1020 debug(F101,"sattr init max","",max);
1023 if (nxtpkt() < 0) /* Got 'em, get next packet number */
1024 return(-1); /* Bad news if we can't */
1026 i = 0; /* Init data field character number */
1028 /* Do each attribute using first-fit method, marking as we go */
1029 /* This is rather long and repititious - could be done more cleverly */
1031 if (atsido && !done[xunchar(c = '.')]) { /* System type */
1032 if (max - i >= x.systemid.len + 2) { /* Enough space ? */
1033 data[i++] = c; /* Yes, add parameter */
1034 data[i++] = tochar(x.systemid.len); /* Add length */
1035 for (j = 0; j < x.systemid.len; j++) /* Add data */
1036 data[i++] = x.systemid.val[j];
1037 numset++; /* Count that we did at least one */
1038 done[xunchar(c)] = 1; /* Mark this attribute as done */
1040 left++; /* so mark this one left to do */
1043 if (atcreo && !done[xunchar(c = '$')]) { /* Creator */
1044 if (max - i >= x.creator.len + 2) { /* Enough space ? */
1046 data[i++] = tochar(x.creator.len);
1047 for (j = 0; j < x.creator.len; j++)
1048 data[i++] = x.creator.val[j];
1050 done[xunchar(c)] = 1;
1054 if (atacto && !done[xunchar(c = '%')]) { /* File account */
1055 if (max - i >= x.account.len + 2) {
1057 data[i++] = tochar(x.account.len);
1058 for (j = 0; j < x.account.len; j++)
1059 data[i++] = x.account.val[j];
1061 done[xunchar(c)] = 1;
1065 if (atfrmo && !done[xunchar(c = '/')]) { /* Packet data format */
1066 if (max - i >= x.recfm.len + 2) {
1068 data[i++] = tochar(x.recfm.len); /* Copy from attr structure */
1069 for (j = 0; j < x.recfm.len; j++)
1070 data[i++] = x.recfm.val[j];
1072 done[xunchar(c)] = 1;
1076 #endif /* STRATUS */
1078 xbin = /* Is the transfer in binary mode? */
1080 binary == XYFT_I || binary == XYFT_L || /* IMAGE or LABELED */
1081 !strncmp(x.recfm.val,"F",1) /* or RECFM=Fxxxxxx */
1083 binary /* User said SET FILE TYPE BINARY */
1087 if (attypo && !done[xunchar(c = '"')]) { /* File type */
1088 if (max - i >= 5) { /* Max length for this field */
1090 if (xbin) { /* Binary */
1091 data[i++] = tochar(2); /* Two characters */
1092 data[i++] = 'B'; /* B for Binary */
1093 data[i++] = '8'; /* 8-bit bytes (note assumption...) */
1095 if (binary != XYFT_L
1101 #endif /* CK_LABELED */
1104 data[i++] = tochar(1); /* One character */
1105 data[i++] = 'A'; /* A = (extended) ASCII with CRLFs */
1107 data[i++] = tochar(3); /* Three characters */
1108 data[i++] = 'A'; /* A = (extended) ASCII with CRLFs */
1109 data[i++] = 'M'; /* M for carriage return */
1110 data[i++] = 'J'; /* J for linefeed */
1111 #endif /* TSOFORMAT */
1114 binary = XYFT_T; /* We automatically detected text */
1118 done[xunchar(c)] = 1;
1124 if (attypo && !xbin && !done[xunchar(c = '/')]) { /* Record format */
1127 data[i++] = tochar(3); /* Three characters */
1128 data[i++] = 'A'; /* A = variable with CRLFs */
1129 data[i++] = 'M'; /* M for carriage return */
1130 data[i++] = 'J'; /* J for linefeed */
1133 #endif /* TSOFORMAT */
1135 if (attypo && !xbin && !done[xunchar(c = '*')]) { /* Text encoding */
1139 data[i++] = tochar(1); /* Length of value is 1 */
1140 data[i++] = 'A'; /* A for ASCII */
1142 done[xunchar(c)] = 1;
1146 if (tcharset == TC_TRANSP || !xfrxla) { /* Transfer character set */
1148 data[i++] = c; /* Encoding */
1149 data[i++] = tochar(1); /* Length of value is 1 */
1150 data[i++] = 'A'; /* A for ASCII (i.e. text) */
1152 done[xunchar(c)] = 1;
1156 tp = tcsinfo[tcharset].designator;
1160 if (max - i >= aln + 2) {
1161 data[i++] = c; /* Encoding */
1162 data[i++] = tochar(aln+1); /* Length of designator. */
1163 data[i++] = 'C'; /* Text in specified charset. */
1164 for (j = 0; j < aln; j++) /* Copy designator */
1165 data[i++] = *tp++; /* Example: *&I6/100 */
1167 done[xunchar(c)] = 1;
1171 done[xunchar(c)] = 1;
1173 #endif /* NOCSETS */
1175 if (atdato && !done[xunchar(c = '#')] && /* Creation date, if any */
1176 (aln = x.date.len) > 0) {
1177 if (max - i >= aln + 2) {
1179 data[i++] = tochar(aln);
1180 for (j = 0; j < aln; j++)
1181 data[i++] = x.date.val[j];
1183 done[xunchar(c)] = 1;
1187 /* File length in K */
1188 if (atleno && !done[xunchar(c = '!')] && x.lengthk > (CK_OFF_T)-1) {
1190 sprintf((char *) &data[i+2],"%ld",x.lengthk); /* safe */
1192 ckstrncpy((char *)&data[i+2],ckfstoa(x.lengthk),32);
1193 #endif /* COMMENT */
1194 aln = (int)strlen((char *)(data+i+2));
1195 if (max - i >= aln + 2) {
1197 data[i+1] = tochar(aln);
1200 done[xunchar(c)] = 1;
1206 /* File length in bytes */
1207 if (atleno && !done[xunchar(c = '1')] && x.length > (CK_OFF_T)-1) {
1209 sprintf((char *) &data[i+2],"%ld",x.length); /* safe */
1211 ckstrncpy((char *)&data[i+2],ckfstoa(x.length),32);
1212 #endif /* COMMENT */
1213 aln = (int)strlen((char *)(data+i+2));
1214 if (max - i >= aln + 2) {
1216 data[i+1] = tochar(aln);
1219 done[xunchar(c)] = 1;
1226 if (atlpro && !done[xunchar(c = ',')] && /* Local protection */
1227 (aln = x.lprotect.len) > 0 && !notafile && xp == 0) {
1228 if (max - i >= aln + 2) {
1230 data[i++] = tochar(aln);
1231 for (j = 0; j < aln; j++)
1232 data[i++] = x.lprotect.val[j];
1234 done[xunchar(c)] = 1;
1238 if (atgpro && !done[xunchar(c = '-')] && /* Generic protection */
1239 (aln = x.gprotect.len) > 0 && !notafile && xp == 0) {
1240 if (max - i >= aln + 2) {
1242 data[i++] = tochar(aln);
1243 for (j = 0; j < aln; j++)
1244 data[i++] = x.gprotect.val[j];
1246 done[xunchar(c)] = 1;
1250 #endif /* CK_PERMS */
1251 if (atblko && fblksiz && !done[xunchar(c = '(')] &&
1252 !notafile && xp == 0) { /* Blocksize */
1253 sprintf((char *) &data[i+2],"%d",fblksiz); /* safe */
1254 aln = (int)strlen((char *)(data+i+2));
1255 if (max - i >= aln + 2) {
1257 data[i+1] = tochar(aln);
1260 done[xunchar(c)] = 1;
1267 if ((rprintf || rmailf) && atdiso && /* MAIL, or REMOTE PRINT? */
1268 !done[xunchar(c = '+')]) {
1269 aln = (int) strlen(optbuf) + 1; /* Options, if any */
1270 if (max - i >= aln + 2) {
1271 data[i++] = c; /* Disposition */
1272 data[i++] = tochar(aln); /* Options, if any */
1274 data[i++] = 'P'; /* P for Print */
1276 data[i++] = 'M'; /* M for Mail */
1277 for (j = 0; optbuf[j]; j++) /* Copy any options */
1278 data[i++] = optbuf[j];
1280 done[xunchar(c)] = 1;
1286 #endif /* NOFRILLS */
1288 if (sendmode == SM_RESEND && !done[xunchar(c = '+')]) {
1290 data[i++] = c; /* Disposition */
1291 data[i++] = tochar(1);
1292 data[i++] = 'R'; /* is RESEND */
1294 done[xunchar(c)] = 1;
1298 #endif /* CK_RESEND */
1300 /* End of Attributes -- to be sent only after sending all others */
1302 debug(F111,"sattr","@",i);
1303 debug(F101,"sattr numset","",numset);
1304 debug(F101,"sattr left","",left);
1306 if ((left == 0 || numset == 0) && !done[xunchar(c = '@')]) {
1308 data[i++] = c; /* End of Attributes */
1309 data[i++] = SP; /* Length 0 */
1310 data[i] = NUL; /* Make sure it's null-terminated */
1312 done[xunchar(c)] = 1;
1316 /* Finished - send the packet off if we have anything in it */
1319 data[i] = NUL; /* Terminate last good field */
1320 debug(F111,"sattr sending",data,left);
1321 aln = (int)strlen((char *)data); /* Get overall length of attributes */
1322 return(spack('A',pktnum,aln,data)); /* Send it */
1327 static char *refused = "";
1329 static char *reason[] = {
1330 "size", "type", "date", "creator", "account", "area", "password",
1331 "blocksize", "access", "encoding", "disposition", "protection",
1332 "protection", "origin", "format",
1333 "sys-dependent", /* 0 */
1351 static int nreason = sizeof(reason) / sizeof(char *);
1355 getreason(s) char *s; { /* Decode attribute refusal reason */
1357 if (rejection == 1) /* Kludge for SET FIL COLL DISCARD */
1358 return("name"); /* when other Kermit doesn't... */
1360 if (*p++ != 'N') return(""); /* Should start with N */
1361 else if ((c = *p) > SP) { /* get reason, */
1362 rejection = c; /* remember it, */
1363 c -= '!'; /* get offset */
1364 p = ((unsigned int) ((CHAR) c) <= (unsigned int) nreason) ?
1372 rsattr(s) CHAR *s; { /* Read response to attribute packet */
1373 debug(F111,"rsattr",s,*s);
1374 if (*s == 'N') { /* If it's 'N' followed by anything, */
1375 refused = getreason((char *)s); /* they are refusing, get reason. */
1376 debug(F110,"rsattr refused",refused,0);
1377 tlog(F110," refused:",refused,0L);
1381 if (sendmode == SM_RESEND && *s == '1') { /* RESEND length */
1382 int n; CK_OFF_T z; CHAR *p;
1385 debug(F101,"rsattr RESEND n","",n);
1387 while (n-- > 0) /* We assume the format is good. */
1388 z = (CK_OFF_T)10 * z + (CK_OFF_T)(*p++ - '0');
1389 debug(F101,"rsattr RESEND z","",z);
1390 if (z > (CK_OFF_T)0) sendstart = z;
1391 debug(F101,"rsattr RESEND sendstart","",sendstart);
1392 if (sendstart > (CK_OFF_T)0)
1393 if (zfseek(sendstart) < 0) /* Input file is already open. */
1396 if (fdispla == XYFD_C)
1397 xxscreen(SCR_FS,0,fsize,""); /* Refresh file transfer display */
1398 #endif /* CK_CURSES */
1400 #endif /* CK_RESEND */
1406 Get attributes from incoming A packet. Returns:
1407 0 on success, file is to be accepted
1408 -1 on failure, file is to be refused
1411 gattr(s, yy) CHAR *s; struct zattr *yy; { /* Read incoming attribute packet */
1414 int aln, i, overflow = 0;
1417 extern int r_cset, axcset[];
1418 #endif /* NOCSETS */
1420 #define ABUFL 40 /* Temporary buffer for conversions */
1422 #define RFBUFL 10 /* Record-format buffer */
1423 static char rfbuf[RFBUFL+1];
1424 #define FTBUFL 10 /* File type buffer */
1425 static char ftbuf[FTBUFL+1];
1426 #define DTBUFL 40 /* File creation date */
1427 static char dtbuf[DTBUFL+1];
1428 #define TSBUFL 10 /* Transfer syntax */
1429 static char tsbuf[TSBUFL+1];
1430 #define IDBUFL 10 /* System ID */
1431 static char idbuf[IDBUFL+1];
1433 #define DSBUFL 100 /* Disposition */
1434 static char dsbuf[DSBUFL+1];
1435 #define SPBUFL 512 /* System-dependent parameters */
1436 static char spbuf[SPBUFL+1];
1438 #define DSBUFL 100 /* Disposition */
1439 static char *dsbuf = NULL;
1440 #define SPBUFL 512 /* System-dependent parameters */
1441 static char *spbuf = NULL;
1442 #endif /* DYNAMIC */
1443 #define RPBUFL 20 /* Attribute reply */
1444 static char rpbuf[RPBUFL+1];
1447 static char lprmbuf[CK_PERMLEN+1];
1448 static char gprmbuf[2];
1449 #endif /* CK_PERMS */
1451 char *rp; /* Pointer to reply buffer */
1452 int retcode; /* Return code */
1454 d = SP; /* Initialize disposition */
1455 ff = filnam; /* Filename returned by rcvfil */
1456 if (fncact == XYFX_R && ofn1x && ofn1[0]) /* But watch out for FC=RENAME */
1457 ff = ofn1; /* because we haven't renamed it yet */
1459 /* Fill in the attributes we have received */
1461 rp = rpbuf; /* Initialize reply buffer */
1462 *rp++ = 'N'; /* for negative reply. */
1464 retcode = 0; /* Initialize return code. */
1466 if (dest == DEST_P) { /* SET DESTINATION PRINTER */
1469 if ((dsbuf = malloc(DSBUFL+1)) == NULL)
1470 fatal("gtattr: no memory for dsbuf");
1471 #endif /* DYNAMIC */
1474 yy->disp.val = dsbuf;
1477 while (c = *s++) { /* Get attribute tag */
1478 aln = xunchar(*s++); /* Length of attribute string */
1480 #ifdef COMMENT /* This case combined with '1' below */
1481 case '!': /* File length in K */
1482 for (i = 0; (i < aln) && (i < ABUFL); i++) /* Copy it */
1484 abuf[i] = '\0'; /* Terminate with null */
1485 if (i < aln) s += (aln - i); /* If field was too long for buffer */
1486 yy->lengthk = ckatofs(abuf); /* Convert to number */
1488 #endif /* COMMENT */
1490 case '/': /* Record format */
1493 for (i = 0; (i < aln) && (i < RFBUFL); i++) /* Copy it */
1495 rfbuf[i] = NUL; /* Terminate with null */
1496 yy->recfm.val = rfbuf; /* Pointer to string */
1497 yy->recfm.len = i; /* Length of string */
1498 if ((rfbuf[0] != 'A') ||
1499 (rfbuf[1] && rfbuf[1] != 'M') ||
1500 (rfbuf[2] && rfbuf[2] != 'J')) {
1501 debug(F110,"gattr bad recfm",rfbuf,0);
1507 case '"': /* File type (text, binary, ...) */
1508 for (i = 0; (i < aln) && (i < FTBUFL); i++)
1509 ftbuf[i] = *s++; /* Copy it into a static string */
1511 if (i < aln) s += (aln - i);
1512 /* TYPE attribute is enabled? */
1514 yy->type.val = ftbuf; /* Pointer to string */
1515 yy->type.len = i; /* Length of string */
1516 debug(F111,"gattr file type", ftbuf, i);
1517 debug(F101,"gattr binary 1","",binary);
1519 if ((*ftbuf != 'A' && *ftbuf != 'B' && *ftbuf != 'I')
1521 /* ... Or our FILE TYPE is LABELED and the incoming file is text... */
1522 || (binary == XYFT_L && *ftbuf == 'A' && !xflg)
1523 #endif /* CK_LABELED */
1525 retcode = -1; /* Reject the file */
1527 if (!opnerr) tlog(F100," refused: type","",0);
1531 The following code moved here from opena() so we set binary mode
1532 as soon as requested by the attribute packet. That way when the first
1533 data packet comes, the mode of transfer can be displayed correctly
1534 before opena() is called.
1536 if (yy->type.val[0] == 'A') { /* Check received attributes. */
1538 if (binary != XYFT_I) /* VMS IMAGE overrides this */
1540 binary = XYFT_T; /* Set current type to Text. */
1541 debug(F101,"gattr binary 2","",binary);
1542 } else if (yy->type.val[0] == 'B') {
1544 if (binary != XYFT_L
1546 && binary != XYFT_U /* VMS special case */
1549 #endif /* CK_LABELED */
1551 if (binary != XYFT_M) /* If not MacBinary... */
1554 debug(F101,"gattr binary 3","",binary);
1559 case '#': /* File creation date */
1560 for (i = 0; (i < aln) && (i < DTBUFL); i++)
1561 dtbuf[i] = *s++; /* Copy it into a static string */
1562 if (i < aln) s += (aln - i);
1564 if (atdati && !xflg) { /* Real file and dates enabled */
1565 yy->date.val = dtbuf; /* Pointer to string */
1566 yy->date.len = i; /* Length of string */
1567 if (fncact == XYFX_U) { /* Receiving in update mode? */
1568 if (zstime(ff,yy,1) > 0) { /* Compare dates */
1569 *rp++ = c; /* Discard if older, reason = date. */
1570 if (!opnerr) tlog(F100," refused: date","",0);
1571 retcode = -1; /* Rejection notice. */
1577 case '(': /* File Block Size */
1578 for (i = 0; (i < aln) && (i < ABUFL); i++) /* Copy it */
1580 abuf[i] = '\0'; /* Terminate with null */
1581 if (i < aln) s += (aln - i);
1583 yy->blksize = atol(abuf); /* Convert to number */
1586 case '*': /* Encoding (transfer syntax) */
1587 for (i = 0; (i < aln) && (i < TSBUFL); i++)
1588 tsbuf[i] = *s++; /* Copy it into a static string */
1589 if (i < aln) s += (aln - i);
1592 xlatype = XLA_NONE; /* Assume no translation */
1593 #endif /* NOCSETS */
1596 yy->encoding.val = tsbuf; /* Pointer to string */
1597 yy->encoding.len = i; /* Length of string */
1598 debug(F101,"gattr encoding",tsbuf,i);
1602 case 'A': /* Normal, nothing special */
1603 tcharset = TC_TRANSP; /* Transparent chars untranslated */
1604 debug(F110,"gattr sets tcharset TC_TRANSP","A",0);
1606 case 'C': /* Specified character set */
1607 if (!xfrxla) { /* But translation disabled */
1608 tcharset = TC_TRANSP;
1609 debug(F110,"gattr sets tcharset TC_TRANSP","C",0);
1613 if (!strcmp("I196",ss)) /* Treat I196 (UTF-8 no level) */
1614 ss = "I190"; /* as I190 (UTF-8 Level 1) */
1615 #endif /* UNICODE */
1616 if (!strcmp("I6/204",ss)) /* Treat "Latin-1 + Euro" */
1617 ss = "I6/100"; /* as I6/100 (regular Latin-1) */
1618 for (i = 0; i < ntcsets; i++) {
1619 if (!strcmp(tcsinfo[i].designator,ss))
1622 debug(F101,"gattr xfer charset lookup","",i);
1623 if (i == ntcsets) { /* If unknown character set, */
1624 debug(F110,"gattr: xfer charset unknown",ss,0);
1625 if (!unkcs) { /* and SET UNKNOWN DISCARD, */
1626 retcode = -1; /* reject the file. */
1629 tlog(F100," refused: character set","",0);
1632 tcharset = tcsinfo[i].code; /* it's known, use it */
1633 debug(F101,"gattr switch tcharset","",tcharset);
1634 debug(F101,"gattr fcharset","",fcharset);
1635 if (r_cset == XMODE_A) { /* Automatic switching? */
1636 if (tcharset > -1 && tcharset <= MAXTCSETS) {
1638 x = axcset[tcharset];
1639 if (x > 0 && x <= MAXFCSETS) {
1641 debug(F101,"gattr switch fcharset","",x);
1645 /* Set up translation type and function */
1646 setxlatype(tcharset,fcharset);
1649 #endif /* NOCSETS */
1650 default: /* Something else. */
1651 debug(F110,"gattr unk encoding attribute",tsbuf,0);
1652 if (!unkcs) { /* If SET UNK DISC */
1655 if (!opnerr) tlog(F100," refused: encoding","",0);
1662 case '+': /* Disposition */
1665 if ((dsbuf = malloc(DSBUFL+1)) == NULL)
1666 fatal("gtattr: no memory for dsbuf");
1667 #endif /* DYNAMIC */
1668 for (i = 0; (i < aln) && (i < DSBUFL); i++)
1669 dsbuf[i] = *s++; /* Copy it into a separate string */
1671 if (i < aln) s += (aln - i);
1672 rs_len = (CK_OFF_T)0;
1673 if (atdisi) { /* We are doing this attribute */
1674 /* Copy it into the attribute structure */
1675 yy->disp.val = dsbuf; /* Pointer to string */
1676 yy->disp.len = i; /* Length of string */
1680 Define NODISPO to disable receipt of mail or print files and of RESEND.
1683 #ifndef datageneral /* MAIL supported only for */
1684 #ifndef OS2 /* UNIX, VMS, and OS-9 */
1688 d != 'M' && /* MAIL */
1693 #endif /* datageneral */
1695 d != 'R' && /* RESEND */
1696 #endif /* CK_RESEND */
1697 d != 'P') { /* PRINT */
1698 retcode = -1; /* Unknown/unsupported disposition */
1700 if (!opnerr) tlog(F101," refused: bad disposition","",d);
1703 debug(F000,"gattr dispos","",dispos);
1710 if (!opnerr) tlog(F100," refused: mail disabled","",0);
1714 #endif /* NOFRILLS */
1720 tlog(F100," refused: print disabled","",0);
1728 rs_len = zgetfs(ff); /* Get length of file */
1729 debug(F111,"gattr RESEND",ff,rs_len);
1731 rs_len &= (long) -512; /* Ensure block boundary if VMS */
1732 rs_len -= 512; /* In case last block not complete */
1733 debug(F111,"gattr rs_len",ff,rs_len);
1736 if (rs_len < 0L) /* Local file doesn't exist */
1738 #endif /* COMMENT */
1740 Another possibility here (or later, really) would be to check if the two
1741 file lengths are the same, and if so, keep the prevailing collision action
1742 as is (note: rs_len == length of existing file; yy->length == fsize ==
1743 length of incoming file). This could be complicated, though, since
1744 (a) we might not have received the length attribute yet, and in fact it
1745 might even be in a subsequent A-packet, yet (b) we have to accept or reject
1746 the Recover attribute now. So better to leave as-is. Anyway, it's probably
1747 more useful this way.
1749 if (rs_len > (CK_OFF_T)0) {
1750 fncsav = fncact; /* Save collision action */
1751 fncact = XYFX_A; /* Switch to APPEND */
1754 retcode = -1; /* This shouldn't happen */
1755 *rp++ = c; /* 'cause it wasn't negotiated. */
1756 if (!opnerr) tlog(F100," refused: resend","",0);
1757 #endif /* CK_RESEND */
1762 if (!opnerr) tlog(F100," refused: NODISPO","",0);
1763 #endif /* NODISPO */
1767 case '.': /* Sender's system ID */
1768 for (i = 0; (i < aln) && (i < IDBUFL); i++)
1769 idbuf[i] = *s++; /* Copy it into a static string */
1771 if (i < aln) s += (aln - i);
1773 yy->systemid.val = idbuf; /* Pointer to string */
1774 yy->systemid.len = i; /* Length of string */
1778 case '0': /* System-dependent parameters */
1780 if (!spbuf && !(spbuf = malloc(SPBUFL)))
1781 fatal("gattr: no memory for spbuf");
1782 #endif /* DYNAMIC */
1783 for (i = 0; (i < aln) && (i < SPBUFL); i++)
1784 spbuf[i] = *s++; /* Copy it into a static string */
1786 if (i < aln) s += (aln - i);
1788 yy->sysparam.val = spbuf; /* Pointer to string */
1789 yy->sysparam.len = i; /* Length of string */
1793 case '!': /* File length in K */
1794 case '1': { /* File length in bytes */
1797 for (i = 0; (i < aln) && (i < ABUFL); i++) /* Copy it */
1799 abuf[i] = '\0'; /* Terminate with null */
1800 if (i < aln) s += (aln - i);
1801 if (rdigits(abuf)) { /* Make sure string is all digits */
1802 xlen = ckatofs(abuf); /* Convert to number */
1803 l2 = ckfstoa(xlen); /* Convert number back to string */
1805 debug(F111,"gattr length",abuf,xlen);
1807 debug(F111,"gattr lengthk",abuf,xlen);
1808 if (ckstrcmp(abuf,l2,-1,1)) { /* This is how we check... */
1809 xlen = (CK_OFF_T)-2; /* -2 = unk, possibly too long */
1811 debug(F111,"gattr overflow",
1812 (c == '1') ? "length" : "lengthk",
1817 debug(F101,"gattr length","",xlen);
1820 debug(F101,"gattr lengthk","",xlen);
1823 /* If the length field is not numeric accept the file */
1824 /* anyway but with an unknown length */
1829 case ',': /* System-dependent protection code */
1830 for (i = 0; (i < aln) && (i < CK_PERMLEN); i++)
1831 lprmbuf[i] = *s++; /* Just copy it - decode later */
1832 lprmbuf[i] = '\0'; /* Terminate with null */
1833 if (i < aln) s += (aln - i);
1835 yy->lprotect.val = (char *)lprmbuf;
1836 yy->lprotect.len = i;
1841 case '-': /* Generic "world" protection code */
1842 gprmbuf[0] = NUL; /* Just 1 byte by definition */
1843 for (i = 0; i < aln; i++) /* But allow for more... */
1844 if (i == 0) gprmbuf[0] = *s++;
1847 yy->gprotect.val = (char *)gprmbuf;
1848 yy->gprotect.len = gprmbuf[0] ? 1 : 0;
1852 #endif /* CK_PERMS */
1854 default: /* Unknown attribute */
1855 s += aln; /* Just skip past it */
1860 /* Check space now, because we also need to know the file type */
1861 /* in case zchkspa() differentiates text and binary (VMS version does) */
1863 if (atleni && !calibrate) { /* Length attribute enabled? */
1864 if (yy->length > (CK_OFF_T)-1) { /* Length-in-bytes attribute rec'd? */
1865 if (!zchkspa(ff,(yy->length))) { /* Check space */
1866 retcode = -1; /* Not enuf */
1868 if (!opnerr) tlog(F100," refused: length bytes","",0);
1870 } else if (yy->lengthk > (CK_OFF_T)-1) { /* Length in K received? */
1872 xlen = yy->lengthk * 1024;
1873 if (!zchkspa(ff,xlen)) {
1874 retcode = -1; /* Check space */
1876 if (!opnerr) tlog(F100," refused: length K","",0);
1880 if (retcode > -1L) { /* Remember the file size */
1881 if (yy->length > (CK_OFF_T)-1) {
1883 } else if (yy->lengthk > (CK_OFF_T)-1 && !overflow) {
1884 fsize = yy->lengthk * 1024L;
1885 } else fsize = yy->length; /* (e.g. -2L) */
1891 sprintf(abuf,"%ld",fsize); /* safe */
1893 ckstrncpy(abuf,ckfstoa(fsize),ABUFL);
1894 #endif /* COMMENT */
1895 debug(F110,"gattr fsize",abuf,0);
1899 if (retcode == 0) rp = rpbuf; /* Null reply string if accepted */
1900 *rp = '\0'; /* End of reply string */
1903 if (d == 'R') { /* Receiving a RESEND? */
1904 debug(F101,"gattr RESEND","",retcode);
1905 /* We ignore retcodes because this overrides */
1906 if (binary != XYFT_B) { /* Reject if not binary */
1907 retcode = -1; /* in case type field came */
1908 ckstrncpy(rpbuf,"N+",RPBUFL); /* after the disposition field */
1909 debug(F111,"gattr RESEND not binary",rpbuf,binary);
1910 } else { /* Binary mode */
1911 retcode = 0; /* Accept the file */
1912 discard = 0; /* If SET FILE COLLISION DISCARD */
1914 sprintf(rpbuf+2,"%ld",rs_len); /* Reply with length of file */
1916 ckstrncpy(rpbuf+2,ckfstoa(rs_len),RPBUFL-2);
1917 #endif /* COMMENT */
1918 rpbuf[0] = '1'; /* '1' means Length in Bytes */
1919 rpbuf[1] = tochar((int)strlen(rpbuf+2)); /* Length of length */
1920 debug(F111,"gattr RESEND OK",rpbuf,retcode);
1923 #endif /* CK_RESEND */
1924 if (retcode == 0 && discard != 0) { /* Do we still have a discard flag? */
1925 ckstrncpy(rpbuf,"N?",RPBUFL); /* Yes, must be filename collision */
1926 retcode = -1; /* "?" = name (reply-only code) */
1928 yy->reply.val = rpbuf; /* Add it to attribute structure */
1929 yy->reply.len = (int)strlen(rpbuf);
1930 if (retcode < 0) { /* If we are rejecting */
1931 discard = 1; /* remember to discard the file */
1932 rejection = rpbuf[1]; /* and use the first reason given. */
1938 debug(F111,"gattr return",rpbuf,retcode);
1942 /* I N I T A T T R -- Initialize file attribute structure */
1945 initattr(yy) struct zattr *yy; {
1946 yy->lengthk = yy->length = (CK_OFF_T)-1;
1951 yy->encoding.val = "";
1952 yy->encoding.len = 0;
1955 yy->systemid.val = "";
1956 yy->systemid.len = 0;
1957 yy->sysparam.val = "";
1958 yy->sysparam.len = 0;
1959 yy->creator.val = "";
1960 yy->creator.len = 0;
1961 yy->account.val = "";
1962 yy->account.len = 0;
1965 yy->password.val = "";
1966 yy->password.len = 0;
1968 yy->xaccess.val = "";
1969 yy->xaccess.len = 0;
1971 if (!ofperms) ofperms = "";
1972 debug(F110,"initattr ofperms",ofperms,0);
1973 yy->lprotect.val = ofperms;
1974 yy->lprotect.len = 0 - strlen(ofperms); /* <-- NOTE! */
1976 A negative length indicates that we have a permissions string but it has
1977 been inherited from a previously existing file rather than picked up
1978 from an incoming A-packet.
1981 yy->lprotect.val = "";
1982 yy->lprotect.len = 0;
1983 #endif /* CK_PERMS */
1984 yy->gprotect.val = "";
1985 yy->gprotect.len = 0;
1991 yy->longname.len = 0 ;
1992 yy->longname.val = "" ;
1997 /* A D E B U -- Write attribute packet info to debug log */
2000 adebu(f,zz) char *f; struct zattr *zz; {
2002 if (deblog == 0) return(0);
2003 debug(F110,"Attributes for incoming file ",f,0);
2004 debug(F101," length in K","",(int) zz->lengthk);
2005 debug(F111," file type",zz->type.val,zz->type.len);
2006 debug(F111," creation date",zz->date.val,zz->date.len);
2007 debug(F111," creator",zz->creator.val,zz->creator.len);
2008 debug(F111," account",zz->account.val,zz->account.len);
2009 debug(F111," area",zz->area.val,zz->area.len);
2010 debug(F111," password",zz->password.val,zz->password.len);
2011 debug(F101," blksize","",(int) zz->blksize);
2012 debug(F111," access",zz->xaccess.val,zz->xaccess.len);
2013 debug(F111," encoding",zz->encoding.val,zz->encoding.len);
2014 debug(F111," disposition",zz->disp.val,zz->disp.len);
2015 debug(F111," lprotection",zz->lprotect.val,zz->lprotect.len);
2016 debug(F111," gprotection",zz->gprotect.val,zz->gprotect.len);
2017 debug(F111," systemid",zz->systemid.val,zz->systemid.len);
2018 debug(F111," recfm",zz->recfm.val,zz->recfm.len);
2019 debug(F111," sysparam",zz->sysparam.val,zz->sysparam.len);
2020 debug(F101," length","",(int) zz->length);
2021 debug(F110," reply",zz->reply.val,0);
2026 /* O P E N A -- Open a file, with attributes. */
2028 This function tries to open a new file to put the arriving data in. The
2029 filename is the one in the srvcmd buffer. File collision actions are:
2030 OVERWRITE (the existing file is overwritten), RENAME (the new file is
2031 renamed), BACKUP (the existing file is renamed), DISCARD (the new file is
2032 refused), UPDATE (the incoming file replaces the existing file only if the
2033 incoming file has a newer creation date).
2035 Returns 0 on failure, nonzero on success.
2037 extern char *rf_err;
2040 opena(f,zz) char *f; struct zattr *zz; {
2042 static struct filinfo fcb; /* Must be static! */
2044 debug(F110,"opena f",f,0);
2045 debug(F101,"opena discard","",discard);
2047 adebu(f,zz); /* Write attributes to debug log */
2049 ffc = (CK_OFF_T)0; /* Init file-character counter */
2052 if (pipesend) /* Receiving to a pipe - easy. */
2053 return(openo(f,zz,&fcb)); /* Just open the pipe. */
2054 #endif /* PIPESEND */
2056 /* Receiving to a file - set up file control structure */
2058 fcb.bs = fblksiz; /* Blocksize */
2060 fcb.cs = fcharset; /* Character set */
2062 fcb.cs = 0; /* Character set */
2063 #endif /* NOCSETS */
2064 fcb.rl = frecl; /* Record Length */
2065 fcb.fmt = frecfm; /* Record Format */
2066 fcb.org = forg; /* Organization */
2067 fcb.cc = fcctrl; /* Carriage control */
2068 fcb.typ = binary; /* Type */
2069 debug(F101,"opena xflg","",xflg);
2070 debug(F101,"opena remfile","",remfile);
2071 debug(F101,"opena remappd","",remappd);
2072 if (xflg && remfile && remappd) /* REMOTE output redirected with >> */
2075 fcb.dsp = (fncact == XYFX_A) ? XYFZ_A : XYFZ_N; /* Disposition */
2076 debug(F101,"opena disp","",fcb.dsp);
2077 fcb.os_specific = ""; /* OS-specific info */
2079 fcb.lblopts = lf_opts; /* Labeled file options */
2082 #endif /* CK_LABELED */
2084 if (zz->disp.len > 0) { /* Incoming file has a disposition? */
2085 debug(F111,"open disposition",zz->disp.val,zz->disp.len);
2086 dispos = (int) (*(zz->disp.val));
2088 if (!dispos && xflg && remfile && remappd) /* REMOTE redirect append ? */
2091 debug(F101,"opena dispos","",dispos);
2093 if (!dispos) { /* No special disposition? */
2094 if (fncact == XYFX_B && ofn1x && ofn2) { /* File collision = BACKUP? */
2095 if (zrename(ofn1,ofn2) < 0) { /* Rename existing file. */
2096 debug(F110,"opena rename fails",ofn1,0);
2097 rf_err = "Can't create backup file";
2099 } else debug(F110,"opena rename ok",ofn2,0);
2101 } else if (dispos == 'R') { /* Receiving a RESEND */
2102 debug(F101,"opena remote len","",zz->length);
2103 debug(F101,"opena local len","",rs_len);
2105 if (fncact == XYFX_R) /* and file collision = RENAME */
2107 #endif /* COMMENT */
2109 f = ofn1; /* use original name. */
2110 if (fncact == XYFX_R) /* if file collision is RENAME */
2111 ckstrncpy(filnam,ofn1,CKMAXPATH+1); /* restore the real name */
2112 xxscreen(SCR_AN,0,0L,f); /* update name on screen */
2113 if (zz->length == rs_len) /* Local and remote lengths equal? */
2114 return(-17); /* Secret code */
2116 debug(F111,"opena [file]=mode: ",f,fcb.dsp);
2117 if (x = openo(f,zz,&fcb)) { /* Try to open the file. */
2119 tlog(F110," local name:",f,0L); /* OK, open, record local name. */
2120 makestr(&prfspec,f); /* New preliminary name */
2123 tlog(F110," local name:",f,0L);
2124 makestr(&prfspec,f);
2126 { /* Log full local pathname */
2127 char *p = NULL, *q = f;
2128 if ((p = malloc(CKMAXPATH+1)))
2129 if (zfnqfp(filnam, CKMAXPATH, p))
2131 tlog(F110," local name:",q,0L);
2132 makestr(&prfspec,q);
2138 if (binary) { /* Log file mode in transaction log */
2139 tlog(F101," mode: binary","",(long) binary);
2140 } else { /* If text mode, check character set */
2141 tlog(F100," mode: text","",0L);
2144 if (fcharset > -1 && fcharset <= MAXFCSETS)
2145 tlog(F110," file character-set:",fcsinfo[fcharset].name,0L);
2146 if (tcharset > -1 && tcharset <= MAXTCSETS)
2147 tlog(F110," xfer character-set:",tcsinfo[tcharset].name,0L);
2149 tlog(F110," character-set:","transparent",0L);
2151 #endif /* NOCSETS */
2152 debug(F111,"opena charset",zz->encoding.val,zz->encoding.len);
2154 debug(F101,"opena binary","",binary);
2158 #endif /* COMMENT */
2159 xxscreen(SCR_FS,0,fsize,"");
2163 Need to turn on multi-tasking console interrupt task here, since multiple
2164 files may be received (huh?) ...
2166 if ((local) && (!quiet)) /* Only do this if local & not quiet */
2167 consta_mt(); /* Start the async read task */
2168 #endif /* datageneral */
2170 } else { /* Did not open file OK. */
2172 rf_err = ck_errstr(); /* Get system error message */
2174 xxscreen(SCR_EM,0,0l,rf_err);
2176 xxscreen(SCR_EM,0,0l,"Can't open output file");
2177 tlog(F110,"Failure to open",f,0L);
2178 tlog(F110,"Error:",rf_err,0L);
2179 debug(F110,"opena error",rf_err,0);
2181 return(x); /* Pass on return code from openo */
2184 /* O P E N C -- Open a command (in place of a file) for output */
2187 openc(n,s) int n; char * s; {
2194 debug(F111,"openc zxcmd",s,x);
2195 o_isopen = (x > 0) ? 1 : 0;
2199 /* C A N N E D -- Check if current file transfer cancelled */
2202 canned(buf) CHAR *buf; {
2203 extern int interrupted;
2204 if (*buf == 'X') cxseen = 1;
2205 if (*buf == 'Z') czseen = 1;
2206 if (czseen || cxseen)
2208 debug(F101,"canned: cxseen","",cxseen);
2209 debug(F101," czseen","",czseen);
2210 return((czseen || cxseen) ? 1 : 0);
2214 /* O P E N I -- Open an existing file for input */
2217 openi(name) char *name; {
2219 extern int fromgetpath;
2220 #endif /* NOSERVER */
2223 extern CHAR *epktmsg;
2225 epktmsg[0] = NUL; /* Initialize error message */
2226 if (memstr || sndarray) { /* Just return if "file" is memory. */
2230 debug(F110,"openi name",name,0);
2231 debug(F101,"openi sndsrc","",sndsrc);
2233 filno = (sndsrc == 0) ? ZSTDIO : ZIFILE; /* ... */
2234 debug(F101,"openi file number","",filno);
2237 /* If I'm a server and CWD is disabled and name is not from GET-PATH... */
2239 if (server && !en_cwd && !fromgetpath) {
2240 zstrip(name,&name2);
2241 if ( /* ... check if pathname included. */
2248 tlog(F110,name,"access denied",0L);
2249 debug(F110,"openi CD disabled",name,0);
2250 ckstrncpy((char *)epktmsg,"Access denied",PKTMSGLEN);
2252 } else name = name2;
2254 #endif /* NOSERVER */
2257 debug(F101,"openi pipesend","",pipesend);
2261 x = zxcmd(ZIFILE,name);
2265 i_isopen = (x > 0) ? 1 : 0;
2267 ckstrncpy((char *)epktmsg,"Command or pipe failure",PKTMSGLEN);
2268 debug(F111,"openi pipesend zxcmd",name,x);
2271 #endif /* PIPESEND */
2278 #endif /* CALIBRATE */
2280 x = zopeni(filno,name); /* Otherwise, try to open it. */
2281 debug(F111,"openi zopeni 1",name,x);
2285 } else { /* If not found, */
2286 char xname[CKMAXPATH]; /* convert the name */
2288 nzrtol(name,xname,fncnv,fnrpath,CKMAXPATH);
2290 zrtol(name,xname); /* to local form and then */
2292 x = zopeni(filno,xname); /* try opening it again. */
2293 debug(F111,"openi zopeni 2",xname,x);
2296 return(1); /* It worked. */
2300 if (s) if (!s) s = NULL;
2301 if (!s) s = "Can't open file";
2302 ckstrncpy((char *)epktmsg,s,PKTMSGLEN);
2303 tlog(F110,xname,s,0L);
2304 debug(F110,"openi failed",xname,0);
2305 debug(F110,"openi message",s,0);
2312 /* O P E N O -- Open a new file for output. */
2315 openo(name,zz,fcb) char *name; struct zattr *zz; struct filinfo *fcb; {
2323 if (stdouf) { /* Receiving to stdout? */
2324 x = zopeno(ZSTDIO,"",zz,NULL);
2326 debug(F101,"openo stdouf zopeno","",x);
2329 debug(F110,"openo: name",name,0);
2331 if (cxseen || czseen || discard) { /* If interrupted, get out before */
2332 debug(F100," open cancelled","",0); /* destroying existing file. */
2333 return(1); /* Pretend to succeed. */
2335 channel = ZOFILE; /* SET DESTINATION DISK or PRINTER */
2338 debug(F101,"openo pipesend","",pipesend);
2342 x = zxcmd(ZOFILE,(char *)srvcmd);
2347 debug(F101,"openo zxcmd","",x);
2350 #endif /* PIPESEND */
2352 if (dest == DEST_S) { /* SET DEST SCREEN... */
2358 dirp = tilde_expand(name);
2359 if (*dirp) ckstrncpy(name,dirp,CKMAXPATH+1);
2362 if (server && !en_cwd) { /* If running as server */
2363 zstrip(name,&name2); /* and CWD is disabled, */
2364 if (strcmp(name,name2)) { /* check if pathname was included. */
2365 tlog(F110,name,"authorization failure",0L);
2366 debug(F110,"openo CD disabled",name,0);
2368 } else name = name2;
2370 if (zopeno(channel,name,zz,fcb) <= 0) { /* Try to open the file */
2372 debug(F110,"openo failed",name,0);
2373 /* tlog(F110,"Failure to open",name,0L); */
2377 debug(F110,"openo ok, name",name,0);
2382 /* O P E N T -- Open the terminal for output, in place of a file */
2385 opent(zz) struct zattr *zz; {
2387 ffc = tfc = (CK_OFF_T)0;
2388 x = zopeno(ZCTERM,"",zz,NULL);
2389 debug(F101,"opent zopeno","",x);
2398 /* O P E N X -- Open nothing (incoming file to be accepted but ignored) */
2401 ckopenx(zz) struct zattr *zz; {
2402 ffc = tfc = (CK_OFF_T)0; /* Reset counters */
2404 debug(F101,"ckopenx fsize","",fsize);
2405 xxscreen(SCR_FS,0,fsize,""); /* Let screen display know the size */
2409 /* C L S I F -- Close the current input file. */
2413 extern int xferstat, success;
2416 fcps(); /* Calculate CPS quickly */
2419 if ((local) && (!quiet)) /* Only do this if local & not quiet */
2420 if (nfils < 1) /* More files to send ... leave it on! */
2422 #endif /* datageneral */
2424 debug(F101,"clsif i_isopen","",i_isopen);
2425 if (i_isopen) { /* If input file is open... */
2426 if (memstr) { /* If input was memory string, */
2427 memstr = 0; /* indicate no more. */
2429 x = zclose(ZIFILE); /* else close input file. */
2433 debug(F101,"clsif zclose","",x);
2434 debug(F101,"clsif success","",success);
2435 debug(F101,"clsif xferstat","",xferstat);
2436 debug(F101,"clsif fsize","",fsize);
2437 debug(F101,"clsif ffc","",ffc);
2438 debug(F101,"clsif cxseen","",cxseen);
2439 debug(F101,"clsif czseen","",czseen);
2440 debug(F101,"clsif discard","",czseen);
2443 if ((cxseen || czseen) && !epktsent) { /* If interrupted */
2444 xxscreen(SCR_ST,ST_INT,0l,""); /* say so */
2446 if (tralog && !tlogfmt)
2447 doxlog(what,psfspec,fsize,binary,1,"Interrupted");
2449 } else if (discard && !epktsent) { /* If I'm refusing */
2450 xxscreen(SCR_ST,ST_REFU,0l,refused); /* say why */
2452 if (tralog && !tlogfmt) {
2454 ckmakmsg(buf,128,"Refused: ",refused,NULL,NULL);
2455 doxlog(what,psfspec,fsize,binary,1,buf);
2458 } else if (!epktrcvd && !epktsent && !cxseen && !czseen) {
2462 if (sendmode == SM_RESEND || sendmode == SM_PSEND)
2464 #endif /* CK_RESEND */
2465 debug(F101,"clsif fstats","",zz);
2466 fstats(); /* Update statistics */
2467 if ( /* Was the whole file sent? */
2469 0 /* Not a reliable check in VMS */
2472 0 /* Probably not for VOS either */
2476 && ((eofmethod != XYEOF_Z && !binary) || binary)
2477 #endif /* CK_CTRLZ */
2478 #endif /* STRATUS */
2481 xxscreen(SCR_ST,ST_INT,0l,"");
2483 if (tralog && !tlogfmt)
2484 doxlog(what,psfspec,fsize,binary,1,"Incomplete");
2488 /* Not yet -- we don't have confirmation from the receiver */
2489 xxscreen(SCR_ST,ST_OK,0l,"");
2490 #endif /* COMMENT */
2492 if (tralog && !tlogfmt)
2493 doxlog(what,psfspec,fsize,binary,0,"");
2499 hcflg = 0; /* Reset flags */
2500 sendstart = (CK_OFF_T)0; /* Don't do this again! */
2503 This prevents a subsequent call to clsof() from deleting the file
2504 when given the discard flag.
2506 *filnam = '\0'; /* and current file name */
2507 #endif /* COMMENT */
2512 /* C L S O F -- Close an output file. */
2514 /* Call with disp != 0 if file is to be discarded. */
2515 /* Returns -1 upon failure to close, 0 or greater on success. */
2518 clsof(disp) int disp; {
2522 fcps(); /* Calculate CPS quickly */
2524 debug(F101,"clsof disp","",disp);
2525 debug(F101,"clsof cxseen","",cxseen);
2526 debug(F101,"clsof success","",success);
2528 debug(F101,"clsof o_isopen","",o_isopen);
2529 if (fncsav != -1) { /* Saved file collision action... */
2530 fncact = fncsav; /* Restore it. */
2531 fncsav = -1; /* Unsave it. */
2534 if ((local) && (!quiet)) /* Only do this if local & not quiet */
2536 #endif /* datageneral */
2537 if (o_isopen && !calibrate) {
2538 if ((x = zclose(ZOFILE)) < 0) { /* Try to close the file */
2539 tlog(F100,"Failure to close",filnam,0L);
2540 xxscreen(SCR_ST,ST_ERR,0l,"Can't close file");
2542 if (tralog && !tlogfmt)
2543 doxlog(what,prfspec,fsize,binary,1,"Can't close file");
2545 } else if (disp) { /* Interrupted or refused */
2546 if (keep == 0 || /* If not keeping incomplete files */
2547 (keep == SET_AUTO && binary == XYFT_T)
2549 if (*filnam && (what & W_RECV)) /* AND we're receiving */
2550 zdelet(filnam); /* ONLY THEN, delete it */
2551 if (what & W_KERMIT) {
2552 debug(F100,"clsof incomplete discarded","",0);
2553 tlog(F100," incomplete: discarded","",0L);
2554 if (!epktrcvd && !epktsent) {
2555 xxscreen(SCR_ST,ST_DISC,0l,"");
2557 if (tralog && !tlogfmt)
2558 doxlog(what,prfspec,fsize,binary,1,"Discarded");
2562 } else { /* Keep incomplete copy */
2563 debug(F100,"clsof fstats 1","",0);
2565 if (!discard) { /* Unless discarding for other reason... */
2566 if (what & W_KERMIT) {
2567 debug(F100,"closf incomplete kept","",0);
2568 tlog(F100," incomplete: kept","",0L);
2571 if (what & W_KERMIT) {
2572 if (!epktrcvd && !epktsent) {
2573 xxscreen(SCR_ST,ST_INC,0l,"");
2575 if (tralog && !tlogfmt)
2576 doxlog(what,prfspec,fsize,binary,1,"Incomplete");
2583 if (o_isopen && x > -1 && !disp) {
2584 debug(F110,"clsof OK",rfspec,0);
2585 makestr(&rfspec,prfspec);
2586 makestr(&rrfspec,prrfspec);
2588 if (!epktrcvd && !epktsent && !cxseen && !czseen) {
2589 xxscreen(SCR_ST,ST_OK,0L,"");
2591 if (tralog && !tlogfmt)
2592 doxlog(what,rfspec,fsize,binary,0,"");
2596 rs_len = (CK_OFF_T)0;
2597 o_isopen = 0; /* The file is not open any more. */
2598 cxseen = 0; /* Reset per-file interruption flag */
2599 return(x); /* Send back zclose() return code. */
2603 tolower(c) char c; { return((c)-'A'+'a'); }
2604 toupper(c) char c; { return((c)-'a'+'A'); }
2605 #endif /* SUNOS4S5 */