1 /*  C K C F N 3  --  Packet buffer management for C-Kermit  */
2 
3 /* (plus assorted functions tacked on at the end) */
4 
5 /*
6   Author: Frank da Cruz <fdc@columbia.edu>,
7   Columbia University Academic Information Systems, New York City.
8 
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.
13 */
14 /*
15  Note -- if you change this file, please amend the version number and date at
16  the top of ckcfns.c accordingly.
17 */
18 
19 #include "ckcsym.h"
20 #include "ckcdeb.h"
21 #include "ckcasc.h"
22 #include "ckcker.h"
23 #include "ckcxla.h"
24 
25 /*  C K M K D I R  --  Create a directory  */
26 /*
27   Call with:
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;
33                 0 means use s as is.
34   Returns:
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.
40 */
41 #ifdef CK_MKDIR
42 static char ckmkdbuf[CKMAXPATH+1];
43 #else
44 #ifdef datageneral
45 static char ckmkdbuf[CKMAXPATH+1];
46 #endif /* datageneral */
47 #endif /* CK_MKDIR */
48 
49 #ifdef CK_MKDIR
50 int
ckmkdir(fc,s,r,m,cvt)51 ckmkdir(fc,s,r,m,cvt) int fc; char * s; char ** r; int m; int cvt; {
52     int x, rc = -2;
53     char tmpbuf[CKMAXPATH+1];
54     char buf2[CKMAXPATH+1];
55     if (!s) s = "";
56     debug(F110,"ckmkdir 1 fc",s,fc);
57     if (!*s) {
58 	ckmakmsg(ckmkdbuf,
59 		 CKMAXPATH+1,
60 		 (fc == 0) ? "mkdir" : "rmdir",
61 		 ": no name given",
62 		 NULL,
63 		 NULL
64 		 );
65 	*r = ckmkdbuf;
66 	return(-2);
67     }
68 #ifdef datageneral
69 /* Come back and make this nicer later if anybody notices */
70     if (fc == 0) {			/* mkdir */
71 	rc = createdir(s,0);
72     } else {				/* rmdir */
73 	/* AOS/VS rmdir() is a no-op. */
74 	ckmakmsg(tmpbuf,CKMAXPATH+1,"delete ",s,NULL,NULL);
75 	debug(F110,"ckmkdir 2",tmpbuf,0);
76 	rc = system(tmpbuf);
77     }
78     *r = NULL;
79 #else /* not datageneral */
80 
81 /* First make sure the name has an acceptable directory-name format */
82 
83 #ifdef VMS
84     {
85 	char *p = s;
86 	int lb = 0, rb = 0, sl = 0;
87 	while (*p) {
88 	    if      (*p == '[' || *p == '<') lb++;   /* Count brackets */
89 	    else if (*p == ']' || *p == '>') rb++;
90 	    else if (*p == '/') 	     sl++;	/* and slashes */
91 	    p++;
92 	}
93 	if (lb != 1 && rb != 1 && sl == 0 && p > s && *(p-1) != ':') {
94 	    /* Probably just a word - convert to VMS format */
95 	    ckmakmsg(buf2,
96 		     CKMAXPATH+1,
97 		     "[",
98 		     (*s == '.') ? "" : ".",
99 		     s,
100 		     "]"
101 		     );
102 	    s = buf2;
103 	} else if (lb == 0 && rb == 0 && sl != 0 && p > s && *(p-1) != ':') {
104 	    int flag = 0;
105 	    /* Seems to be in UNIX format */
106 	    x = strlen(s);
107 	    if (x > 0 && s[x-1] != '/')
108 	      flag = 1;
109 	    ckmakmsg(buf2,CKMAXPATH+1,s,flag ? "/" : "",NULL,NULL);
110 	    s = buf2;
111 	}
112 	if (s == buf2) {
113 	    ckstrncpy(tmpbuf,s,CKMAXPATH+1);
114 	    s = tmpbuf;
115 	}
116 	debug(F110,"ckmkdir 2+VMS",s,0);
117     }
118 #else
119 #ifdef UNIXOROSK
120 #ifdef DTILDE
121     s = tilde_expand(s);
122 #endif /* DTILDE */
123     ckstrncpy(tmpbuf,s,CKMAXPATH+1);
124     s = tmpbuf;
125     x = strlen(s);
126     if (x > 0 && s[x-1] != '/') {	/* Must end in "/" for zmkdir() */
127 	s[x] = '/';
128 	s[x+1] = NUL;
129 	debug(F110,"ckmkdir 2+UNIXOROSK",s,0);
130     }
131 #else /* UNIXOROSK */
132 #ifdef OS2
133     ckstrncpy(tmpbuf,s,CKMAXPATH+1);
134     s = tmpbuf;
135     x = strlen(s);
136     if (fc == 0 && x > 0 && s[x-1] != '/') { /* Must end in "/" for zmkdir() */
137 	s[x] = '/';
138 	s[x+1] = NUL;
139 	debug(F110,"ckmkdir 2+OS2",s,0);
140     }
141 #endif /* OS2 */
142 #endif /* UNIXOROSK */
143 #endif /* VMS */
144 #ifdef NZLTOR
145     /* Server is calling us, so convert to local format if necessary */
146     if (cvt) {
147 	nzrtol(s,(char *)buf2,1,PATH_ABS,CKMAXPATH);
148 	s = buf2;
149 	debug(F110,"ckmkdir 3",s,0);
150     }
151 #endif /* NZLTOR */
152     debug(F110,"ckmkdir 4",s,0);
153     if (fc == 0) {			/* Making */
154 #ifdef CK_MKDIR
155 	rc = zmkdir(s);
156 #else
157 #ifdef NT
158 	rc = _mkdir(s);
159 #else
160 	rc = mkdir(s,0777);
161 #endif /* NT */
162 #endif /* CK_MKDIR */
163     } else {				/* Removing */
164 #ifdef ZRMDIR
165 	rc = zrmdir(s);
166 #else
167 #ifdef NT
168 	rc = _rmdir(s);
169 #else
170 #ifdef OSK
171 	rc = -2;
172 #else
173 	rc = rmdir(s);
174 #endif /* OSK */
175 #endif /* NT */
176 #endif /* ZRMDIR */
177     }
178 #endif /* datageneral */
179     debug(F101,"ckmkdir rc","",rc);
180     if (rc == -2) {
181 	ckmakmsg(ckmkdbuf,
182 		 CKMAXPATH,
183 		 "Directory ",
184 		 (fc == 0) ? "creation" : "removal",
185 		 "not implemented in this version of C-Kermit",
186 		 NULL
187 		 );
188 	*r = ckmkdbuf;
189 	if (m) printf("%s\n",*r);
190     } else if (rc < 0) {
191 	if (m) perror(s);
192 	ckmakmsg(ckmkdbuf,CKMAXPATH,s,": ",ck_errstr(),NULL);
193 	*r = ckmkdbuf;
194     } else if (fc == 0 && zfnqfp(s,CKMAXPATH,ckmkdbuf)) {
195 	*r = ckmkdbuf;
196     } else if (fc != 0) {
197 	ckmakmsg(ckmkdbuf,CKMAXPATH,s,": removed",NULL,NULL);
198 	*r = ckmkdbuf;
199     }
200     return(rc);
201 }
202 #endif /* CK_MKDIR */
203 
204 #ifndef NOXFER				/* Rest of this file... */
205 
206 #ifndef NODISPO
207 #ifdef pdp11
208 #define NODISPO
209 #endif /* pdpd11 */
210 #endif /* NODISPO */
211 
212 extern int pipesend;
213 #ifdef PIPESEND
214 extern char ** sndfilter;
215 #endif /* PIPESEND */
216 
217 extern int unkcs, wmax, wcur, discard, bctu, bctl, local, fdispla, what,
218     sendmode, opnerr, dest, epktrcvd, epktsent, filestatus, eofmethod, dispos,
219     fncnv, fnrpath;
220 
221 extern char * ofn2;
222 extern char * rfspec, * sfspec, * prfspec, * psfspec, * rrfspec, * prrfspec;
223 extern char ofn1[];
224 extern int ofn1x;
225 extern char * ofperms;
226 
227 #ifdef VMS
228 extern int batch;
229 #else
230 extern int backgrd;
231 #endif /* VMS */
232 
233 extern int xflg, remfile, remappd;
234 extern CHAR *data;
235 extern char filnam[];
236 #ifndef NOFRILLS
237 extern int rprintf, rmailf;		/* REMOTE MAIL, PRINT */
238 char optbuf[OPTBUFLEN];			/* Options for MAIL or REMOTE PRINT */
239 #endif /* NOFRILLS */
240 extern int wslots;
241 extern int fblksiz, frecl, forg, frecfm, fncact, fncsav, fcctrl, lf_opts;
242 extern CHAR * srvcmd;
243 extern int srvcmdlen;
244 
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;
249 
250 /* Attributes in/out enabled flags */
251 
252 extern int
253   atenci, atenco, atdati, atdato, atleni, atleno, atblki, atblko,
254   attypi, attypo, atsidi, atsido, atsysi, atsyso, atdisi, atdiso;
255 
256 #ifdef CK_PERMS
257 extern int atlpri, atlpro, atgpri, atgpro;
258 #endif /* CK_PERMS */
259 
260 #ifdef STRATUS
261 extern int atfrmi, atfrmo, atcrei, atcreo, atacti, atacto;
262 #endif /* STRATUS */
263 
264 #ifdef datageneral
265 extern int quiet;
266 #endif /* datageneral */
267 
268 extern long filcnt;
269 extern CK_OFF_T fsize, ffc, tfc, sendstart, calibrate;
270 CK_OFF_T rs_len;
271 
272 #ifndef NOCSETS
273 _PROTOTYP (VOID setxlate, (void));
274 extern int tcharset, fcharset;
275 extern int ntcsets, xlatype, xfrxla;
276 extern struct csinfo tcsinfo[], fcsinfo[];
277 #endif /* NOCSETS */
278 
279 /* Variables global to Kermit that are defined in this module */
280 
281 #ifdef CKXXCHAR				/* DOUBLE / IGNORE char table */
282 int dblflag = 0;
283 int ignflag = 0;
284 short dblt[256] = {
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
293 };
294 #endif /* CKXXCHAR */
295 
296 int winlo;				/* packet number at low window edge  */
297 
298 int sbufnum;				/* number of free buffers */
299 int dum001 = 1234;			/* protection... */
300 int sbufuse[MAXWS];			/* buffer in-use flag */
301 int dum003 = 1111;
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 */
308 
309 int o_isopen = 0, i_isopen = 0;		/* Input & output files are open */
310 
311 #ifdef DYNAMIC
312 struct pktinfo *s_pkt = NULL;		/* array of pktinfo structures */
313 struct pktinfo *r_pkt = NULL;		/* array of pktinfo structures */
314 #else
315 struct pktinfo s_pkt[MAXWS];		/* array of pktinfo structures */
316 struct pktinfo r_pkt[MAXWS];		/* array of pktinfo structures */
317 #endif /* DYNAMIC */
318 
319 #ifdef DEBUG
320 char xbuf[200];				/* For debug logging */
321 #endif /* DEBUG */
322 
323 #ifdef DYNAMIC
324 CHAR *bigsbuf = NULL, *bigrbuf = NULL;
325 #else
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 */
331 #endif
332 int bigsbsiz = SBSIZ;			/* Sizes of big send & rcv buffers. */
333 int bigrbsiz = RBSIZ;
334 
335 #ifdef VMS
336 int zchkpath(char *s);
337 #endif /* VMS */
338 
339 /* FUNCTIONS */
340 
341 VOID
dofast()342 dofast() {
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[];
348 
349     if (maxpktsiz < 40)			/* Long packet length */
350       maxpktsiz = 40;
351     else if (maxpktsiz > 4000)
352       maxpktsiz = 4000;
353     wslotr = maxbufsiz / maxpktsiz;
354     if (wslotr > MAXWS)			/* Window slots */
355       wslotr = MAXWS;
356     if (wslotr > 30)
357       wslotr = 30;
358     else if (wslotr < 1)
359       wslotr = 1;
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);
364 #ifdef IRIX
365 #ifndef IRIX65
366     /* IRIX Telnet server chops off writes longer than 4K */
367     spsiz = spmax = spsizr = urpsiz;
368     debug(F101,"doarg Q IRIX spsiz","",spsiz);
369     spsizf = 1;
370 #endif /* IRIX65 */
371 #endif /* IRIX */
372 #ifdef CK_SPEED
373     setprefix(PX_CAU);			/* Cautious unprefixing */
374 #endif /* CK_SPEED */
375 }
376 
377 
378 /* For sanity, use "i" for buffer slots, "n" for packet numbers. */
379 
380 /* I N I B U F S */
381 
382 /*
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.
392 */
393 
394 CHAR *bigbufp = NULL;
395 
396 int
inibufs(s,r)397 inibufs(s,r) int s, r; {
398 #ifdef DYNAMIC
399     unsigned
400       int size;
401 #ifdef OS2
402     unsigned		/* Don't you wish everybody had unsigned long... */
403 #endif /* OS2 */
404       long z;
405     int x;
406 
407     debug(F101,"inibufs s","",s);
408     debug(F101,"inibufs r","",r);
409 
410     if (s < 80 || r < 80) return(-1);	/* Validate arguments. */
411 
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");
415     }
416     for (x = 0; x < MAXWS; x++)
417       s_pkt[x].pk_adr = NULL;		/* Initialize addresses */
418 
419     if (!r_pkt) {
420 	if (!(r_pkt = (struct pktinfo *) malloc(sizeof(struct pktinfo)*MAXWS)))
421 	  fatal("ini_pkts: no memory for s_pkt");
422     }
423     for (x = 0; x < MAXWS; x++)
424       r_pkt[x].pk_adr = NULL;		/* Initialize addresses */
425 
426     if (!srvcmd) {			/* Allocate srvcmd buffer */
427 	srvcmd = (CHAR *) malloc(r + 100);
428 	if (!srvcmd) return(-1);
429 	srvcmdlen = r + 99;
430 	*srvcmd = NUL;
431     }
432     if (bigbufp) {			/* Free previous buffers, if any. */
433 	free(bigbufp);
434 	bigbufp = NULL;
435     }
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);
442 	size = 65535;
443     }
444 
445     /* Try to get the space.  If malloc fails, try to get a little less. */
446     /* (Obviously, this algorithm can be refined.) */
447 
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. */
452 	  return(-1);
453     }
454     debug(F101,"inibufs size 2","",size); /* OK, we got some space. */
455 
456 /*
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.
464 */
465 #define FACTOR 20L
466     z = ( (long) s * FACTOR ) / ( (long) s + (long) r );
467     x = ( z * ( (long) size / FACTOR ) );
468     if (x < 0) return(-1);		/* Catch overflow */
469 
470     bigsbsiz = x - 5;			/* Size of send buffer */
471     bigsbuf = bigbufp;			/* Address of send buffer */
472     debug(F101,"inibufs bigsbsiz","",bigsbsiz);
473 
474     bigrbsiz = size - x - 5;		/* Size of receive buffer */
475     bigrbuf = bigbufp + x;		/* Addresss of receive buffer */
476     debug(F101,"inibufs bigrbsiz","",bigrbsiz);
477 
478     return(0);				/* Success */
479 #else					/* No dynamic allocation */
480     bigsbsiz = SBSIZ;			/* Just use the symbols */
481     bigrbsiz = RBSIZ;			/* ... */
482     return(0);				/* Success. */
483 #endif /* DYNAMIC */
484 }
485 
486 
487 /* M A K E B U F  --  Makes and clears a new buffers.  */
488 
489 /* Call with: */
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 */
494 
495 /* Subdivides the big buffer into "slots" buffers. */
496 
497 /* Returns: */
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. */
502 
503 int
makebuf(slots,bufsiz,buf,xx)504 makebuf(slots,bufsiz,buf,xx)
505 /* makebuf */ int slots, bufsiz; CHAR buf[]; struct pktinfo *xx; {
506 
507     CHAR *a;
508     int i, size;
509 
510     debug(F101,"makebuf","",slots);
511     debug(F101,"makebuf bufsiz","",bufsiz);
512     debug(F101,"makebuf MAXWS","",MAXWS);
513 
514     if (slots > MAXWS || slots < 1) return(-1);
515     if (bufsiz < slots * 10 ) return(-2);
516 
517     size = bufsiz / slots;		/* Divide up the big buffer. */
518     a = buf;				/* Address of first piece. */
519 
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 */
530     }
531     return(size);
532 }
533 
534 /*  M A K S B U F  --  Makes the send-packet buffer  */
535 
536 int
mksbuf(slots)537 mksbuf(slots) int slots; {
538     int i, x;
539     sbufnum = 0;
540     if ((x = makebuf(slots,bigsbsiz,bigsbuf,s_pkt)) < 0) {
541 	debug(F101,"mksbuf makebuf return","",x);
542 	return(x);
543     }
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. */
547         sacktbl[i] = 0;
548     }
549     for (i = 0; i < MAXWS; i++)
550       sbufuse[i] = 0;			/* Mark each buffer as free */
551     sbufnum = slots;
552     wcur = 0;
553     return(x);
554 }
555 
556 /*  M A K R B U F  --  Makes the receive-packet buffer  */
557 
558 int
mkrbuf(slots)559 mkrbuf(slots) int slots; {
560     int i, x;
561     rbufnum = 0;
562     if ((x = makebuf(slots,bigrbsiz,bigrbuf,r_pkt)) < 0) {
563 	debug(F101,"mkrbuf makebuf return","",x);
564 	return(x);
565     }
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. */
569     }
570     for (i = 0; i < MAXWS; i++)
571       rbufuse[i] = 0;			/* Mark each buffer as free */
572     rbufnum = slots;
573     wcur = 0;
574     return(x);
575 }
576 
577 /*  W I N D O W  --  Resize the window to n  */
578 
579 int
window(n)580 window(n) int 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);
585     wslots = n;
586 #ifdef DEBUG
587     if (deblog) dumpsbuf();
588     if (deblog) dumprbuf();
589 #endif /* DEBUG */
590     return(0);
591 }
592 
593 /*  G E T S B U F  --  Allocate a send-buffer.  */
594 
595 /*  Call with packet sequence number to allocate buffer for. */
596 /*  Returns: */
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. */
602 
603 int
getsbuf(n)604 getsbuf(n) int n; {			/* Allocate a send-buffer */
605     int i;
606     CHAR * p = NULL;
607     if (n < 0 || n > 63) {
608 	debug(F101,"getsbuf bad arg","",n);
609 	return(-4);	/* Bad argument */
610     }
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);
628 	  data = p;
629 	  if (!data) {
630 	      debug(F100,"getsbuf data == NULL","",0);
631               return(-3);
632           }
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. */
637       }
638     sbufnum = 0;			/* Didn't find one. */
639     return(-3);				/* Shouldn't happen! */
640 }
641 
642 int
getrbuf()643 getrbuf() {				/* Allocate a receive buffer */
644     int i;
645 #ifdef COMMENT
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);
652 #endif /* COMMENT */
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. */
665       }
666     /* debug(F101,"getrbuf foulup","",i); */
667     rbufnum = 0;			/* Didn't find one. */
668     return(-3);				/* Shouldn't happen! */
669 }
670 
671 /*  F R E E S B U F  --  Free send-buffer for given packet sequence number */
672 
673 /*  Returns:  */
674 /*   1 upon success  */
675 /*  -1 if specified buffer does not exist */
676 
677 int
freesbuf(n)678 freesbuf(n) int n; {			/* Release send-buffer for packet n. */
679     int i;
680 
681     debug(F101,"freesbuf","",n);
682     if (n < 0 || n > 63)		/* No such packet. */
683       return(-1);
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. */
691     } else {
692 	debug(F101," sseqtbl[n]","",sseqtbl[n]);
693 	return(-1);
694     }
695 
696 /* The following is done only so dumped buffers will look right. */
697 
698     if (1) {
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. */
704     }
705     return(1);
706 }
707 
708 int
freerbuf(i)709 freerbuf(i) int i; {			/* Release receive-buffer slot "i". */
710     int n;
711 
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.... */
715 
716     /* debug(F101,"freerbuf, slot","",i); */
717     if (i < 0 || i >= wslots) {		/* No such slot. */
718 	debug(F101,"freerbuf no such slot","",i);
719 	return(-1);
720     }
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 */
724       rseqtbl[n] = -1;
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);
731     }
732 
733 /* The following is done only so dumped buffers will look right. */
734 
735     if (1) {
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. */
741     }
742     return(1);
743 }
744 
745 /* This is like freerbuf, except it's called with a packet sequence number */
746 /* rather than a packet buffer index. */
747 
748 VOID
freerpkt(seq)749 freerpkt(seq) int seq; {
750     int k;
751     debug(F101,"freerpkt seq","",seq);
752     k = rseqtbl[seq];
753     /* debug(F101,"freerpkt k","",k); */
754     if (k > -1) {
755 	k = freerbuf(k);
756 	/* debug(F101,"freerpkt freerbuf","",k); */
757     }
758 }
759 
760 
761 /*  C H K W I N  --  Check if packet n is in window. */
762 
763 /*  Returns: */
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.  */
768 
769 /* Call with packet number to check (n), lowest packet number in window */
770 /* (bottom), and number of slots in window (slots).  */
771 
772 int
chkwin(n,bottom,slots)773 chkwin(n,bottom,slots) int n, bottom, slots; {
774     int top, prev;
775 
776     debug(F101,"chkwin packet","",n);
777     debug(F101,"chkwin winlo","",bottom);
778     debug(F101,"chkwin slots","",slots);
779 
780 /* First do the easy and common cases, where the windows are not split. */
781 
782     if (n < 0 || n > 63 || bottom < 0 || bottom > 63)
783       return(-2);
784 
785     if (n == bottom) return(0);		/* In a perfect world... */
786 
787     top = bottom + slots;		/* Calculate window top. */
788     if (top < 64 && n < top && n >= bottom)
789       return(0);			/* In current window. */
790 
791     prev = bottom - slots;		/* Bottom of previous window. */
792     if (prev > -1 && n < bottom && n > prev)
793       return(1);			/* In previous. */
794 
795 /* Now consider the case where the current window is split. */
796 
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. */
804 	    else
805 	      return(-1);		/* Not in previous window. */
806 	}
807     }
808 
809 /* Now the case where current window not split, but previous window is. */
810 
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. */
818     }
819 
820 /* It's not in the current window, and not in the previous window... */
821 
822     return(-1);				/* So it's not in any window. */
823 }
824 
825 int
dumpsbuf()826 dumpsbuf() {				/* Dump send-buffers */
827 #ifdef DEBUG
828     int j, x, z;			/* to debug log. */
829 
830     if (! deblog) return(0);
831     x = zsoutl(ZDFILE,"SEND BUFFERS:");
832     if (x < 0) {
833 	deblog = 0;
834 	return(0);
835     }
836     x=zsoutl(ZDFILE,"buffer inuse address length data type seq flag retries");
837     if (x < 0) {
838 	deblog = 0;
839 	return(0);
840     }
841     for (j = 0; j < wslots; j++) {
842 	if (!sbufuse[j])
843 	  continue;
844 	z = ((unsigned long)(s_pkt[j].bf_adr)) & 0xffff;
845 
846 	sprintf(xbuf,			/* safe (200) */
847 		"%4d%6d%10d%5d%6d%4c%5d%6d\n",
848 		j,
849 		sbufuse[j],
850 		/* Avoid warnings when addresses are bigger than ints */
851 		z,
852 		s_pkt[j].bf_len,
853 		s_pkt[j].pk_len,
854 		s_pkt[j].pk_typ,
855 		s_pkt[j].pk_seq,
856 		s_pkt[j].pk_rtr
857 		);
858 	if (zsout(ZDFILE,xbuf) < 0)  {
859 	    deblog = 0;
860 	    return(0);
861 	}
862 	if (s_pkt[j].pk_adr) {
863 	    x = (int)strlen((char *) s_pkt[j].pk_adr);
864 	    if (x)
865 	      sprintf(xbuf,		/* safe (checked) */
866 		      "[%.72s%s]\n",s_pkt[j].pk_adr, x > 72 ? "..." : "");
867 	    else
868 	      sprintf(xbuf,"[(empty string)]\n"); /* safe (200) */
869 	} else {
870 	    sprintf(xbuf,"[(null pointer)]\n");	/* safe (200) */
871 	}
872 	if (zsout(ZDFILE,xbuf) < 0) {
873 	    deblog = 0;
874 	    return(0);
875 	}
876     }
877     sprintf(xbuf,"free: %d, winlo: %d\n", sbufnum, winlo); /* safe (200) */
878     if (zsout(ZDFILE,xbuf) < 0) {
879 	deblog = 0;
880 	return(0);
881     }
882 #endif /* DEBUG */
883     return(0);
884 }
885 int
dumprbuf()886 dumprbuf() {				/* Dump receive-buffers */
887 #ifdef DEBUG
888     int j, x, z;
889     if (! deblog) return(0);
890     if (zsoutl(ZDFILE,"RECEIVE BUFFERS:") < 0) {
891 	deblog = 0;
892 	return(0);
893     }
894     x=zsoutl(ZDFILE,"buffer inuse address length data type seq flag retries");
895     if (x < 0) {
896 	deblog = 0;
897 	return(0);
898     }
899     for ( j = 0; j < wslots; j++ ) {
900 	if (!rbufuse[j])
901 	  continue;
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",
905 		j,
906 		rbufuse[j],
907 		/* Avoid warnings when addresses are bigger than ints */
908 		z,
909 		r_pkt[j].bf_len,
910 		r_pkt[j].pk_len,
911 		r_pkt[j].pk_typ,
912 		r_pkt[j].pk_seq,
913 		r_pkt[j].pk_rtr
914 		);
915 	if (zsout(ZDFILE,xbuf) < 0) {
916 	    deblog = 0;
917 	    return(0);
918 	}
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)  {
923 	    deblog = 0;
924 	    return(0);
925 	}
926     }
927     sprintf(xbuf,"free: %d, winlo: %d\n", rbufnum, winlo); /* safe (200) */
928     if (zsout(ZDFILE,xbuf) < 0)  {
929 	deblog = 0;
930 	return(0);
931     }
932 #endif /* DEBUG */
933     return(0);
934 }
935 
936 /*  S A T T R  --  Send an Attribute Packet  */
937 
938 /*
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.
942 
943   Call with:
944     xp == 0 if we're sending a real file (F packet), or:
945     xp != 0 for screen data (X packet).
946   And:
947     flag == 1 for first A packet
948     flag == 0 for subsequent A packets.
949   Returns:
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.
954 */
955 
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... */
959 
960 int
sattr(xp,flag)961 sattr(xp, flag) int xp, flag; {		/* Send Attributes */
962 
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];
967 
968     extern char * cksysid;
969 
970     /* Some extra flags are used because the "done" array is sparse */
971 
972     int i, j, rc, aln, left = 0, numset = 0, xbin = 0; /* Workers */
973     int notafile = 0;
974     char *tp, c;
975 
976     notafile = sndarray || pipesend ||
977 #ifdef PIPESEND
978       sndfilter ||
979 #endif /* PIPESEND */
980 	calibrate;
981 
982     debug(F101,"sattr flag","",flag);
983     if (!flag)				/* No more attributes to send */
984       if (done[xunchar('@')])
985 	return(0);
986 
987     /* Initialize Attribute mechanism */
988 
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 */
992 	  done[j] = 0;
993 	max = maxdata();		/* Get maximum data field length */
994 	if (notafile || xp == 1) {	/* Is it not a real file? */
995 	    extern char * zzndate();
996 	    char * p;
997 	    int i;
998 #ifdef CALIBRATE
999 	    if (calibrate) {		/* Calibration run... */
1000 		x.lengthk = calibrate / 1024L; /* We know the length */
1001 		x.length = calibrate;
1002 	    }
1003 #endif /* CALIBRATE */
1004 	    x.systemid.val = cksysid;	/* System ID */
1005 	    x.systemid.len = (int)strlen(cksysid);
1006 	    ckstrncpy(xdate,zzndate(),24);
1007 	    xdate[8] = SP;
1008 	    ztime(&p);
1009 	    for (i = 11; i < 19; i++)	/* copy hh:mm:ss */
1010 	      xdate[i - 2] = p[i];	/* to xdate */
1011 	    xdate[17] = NUL;		/* terminate */
1012 	    x.date.val = xdate;
1013 	    x.date.len = 17;
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 */
1019 	      return(0);
1020 	    debug(F101,"sattr init max","",max);
1021 	}
1022     }
1023     if (nxtpkt() < 0)			/* Got 'em, get next packet number */
1024       return(-1);			/* Bad news if we can't */
1025 
1026     i = 0;				/* Init data field character number */
1027 
1028     /* Do each attribute using first-fit method, marking as we go */
1029     /* This is rather long and repititious - could be done more cleverly */
1030 
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 */
1039 	} else				/* No */
1040 	  left++;			/* so mark this one left to do */
1041     }
1042 #ifdef STRATUS
1043     if (atcreo && !done[xunchar(c = '$')]) { /* Creator */
1044 	if (max - i >= x.creator.len + 2) { /* Enough space ? */
1045 	    data[i++] = c;
1046 	    data[i++] = tochar(x.creator.len);
1047 	    for (j = 0; j < x.creator.len; j++)
1048 	      data[i++] = x.creator.val[j];
1049 	    numset++;
1050 	    done[xunchar(c)] = 1;
1051 	} else
1052 	  left++;
1053     }
1054     if (atacto && !done[xunchar(c = '%')]) { /* File account */
1055 	if (max - i >= x.account.len + 2) {
1056 	    data[i++] = c;
1057 	    data[i++] = tochar(x.account.len);
1058 	    for (j = 0; j < x.account.len; j++)
1059 	      data[i++] = x.account.val[j];
1060 	    numset++;
1061 	    done[xunchar(c)] = 1;
1062 	} else
1063 	  left++;
1064     }
1065     if (atfrmo && !done[xunchar(c = '/')]) { /* Packet data format */
1066 	if (max - i >= x.recfm.len + 2) {
1067 	    data[i++] = c;
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];
1071 	    numset++;
1072 	    done[xunchar(c)] = 1;
1073 	} else
1074 	  left++;
1075     }
1076 #endif /* STRATUS */
1077 
1078     xbin =				/* Is the transfer in binary mode? */
1079 #ifdef VMS
1080       binary == XYFT_I || binary == XYFT_L || /* IMAGE or LABELED */
1081 	!strncmp(x.recfm.val,"F",1)	/* or RECFM=Fxxxxxx */
1082 #else
1083       binary				/* User said SET FILE TYPE BINARY  */
1084 #endif /* VMS */
1085 	;
1086 
1087     if (attypo && !done[xunchar(c = '"')]) { /* File type */
1088 	if (max - i >= 5) {		/* Max length for this field */
1089 	    data[i++] = c;
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...) */
1094 #ifdef CK_LABELED
1095 		if (binary != XYFT_L
1096 #ifdef VMS
1097 		    && binary != XYFT_I
1098 #endif /* VMS */
1099 		    )
1100 		  binary = XYFT_B;
1101 #endif /* CK_LABELED */
1102 	    } else {			/* Text */
1103 #ifdef TSOFORMAT
1104 		data[i++] = tochar(1);	/*  One character */
1105 		data[i++] = 'A';	/*  A = (extended) ASCII with CRLFs */
1106 #else
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 */
1112 
1113 #ifdef VMS
1114 		binary = XYFT_T;	/* We automatically detected text */
1115 #endif /* VMS */
1116 	    }
1117 	    numset++;
1118 	    done[xunchar(c)] = 1;
1119 	} else
1120 	  left++;
1121     }
1122 
1123 #ifdef TSOFORMAT
1124     if (attypo && !xbin && !done[xunchar(c = '/')]) { /* Record format */
1125 	if (max - i >= 5) {
1126 	    data[i++] = c;
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 */
1131 	}
1132     }
1133 #endif /* TSOFORMAT */
1134 
1135     if (attypo && !xbin && !done[xunchar(c = '*')]) { /* Text encoding */
1136 #ifdef NOCSETS
1137 	if (max - i >= 3) {
1138 	    data[i++] = c;
1139 	    data[i++] = tochar(1);	/* Length of value is 1 */
1140 	    data[i++] = 'A';		/* A for ASCII */
1141 	    numset++;
1142 	    done[xunchar(c)] = 1;
1143 	} else
1144 	  left++;
1145 #else
1146 	if (tcharset == TC_TRANSP || !xfrxla) {	/* Transfer character set */
1147 	    if (max - i >= 3) {
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) */
1151 		numset++;
1152 		done[xunchar(c)] = 1;
1153 	    } else
1154 	      left++;
1155 	} else {
1156 	    tp = tcsinfo[tcharset].designator;
1157 	    if (!tp) tp = "";
1158 	    aln = strlen(tp);
1159 	    if (aln > 0) {
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 */
1166 		    numset++;
1167 		    done[xunchar(c)] = 1;
1168 		} else
1169 		  left++;
1170 	    } else
1171 	      done[xunchar(c)] = 1;
1172 	}
1173 #endif /* NOCSETS */
1174     }
1175     if (atdato && !done[xunchar(c = '#')] && /* Creation date, if any */
1176 	(aln = x.date.len) > 0) {
1177 	if (max - i >= aln + 2) {
1178 	    data[i++] = c;
1179 	    data[i++] = tochar(aln);
1180 	    for (j = 0; j < aln; j++)
1181 	      data[i++] = x.date.val[j];
1182 	    numset++;
1183 	    done[xunchar(c)] = 1;
1184 	} else
1185 	  left++;
1186     }
1187     /* File length in K */
1188     if (atleno && !done[xunchar(c = '!')] && x.lengthk > (CK_OFF_T)-1) {
1189 #ifdef COMMENT
1190 	sprintf((char *) &data[i+2],"%ld",x.lengthk); /* safe */
1191 #else
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) {
1196 	    data[i] = c;
1197 	    data[i+1] = tochar(aln);
1198 	    i += aln + 2;
1199 	    numset++;
1200 	    done[xunchar(c)] = 1;
1201 	} else {
1202 	    data[i] = NUL;
1203 	    left++;
1204 	}
1205     }
1206     /* File length in bytes */
1207     if (atleno && !done[xunchar(c = '1')] && x.length > (CK_OFF_T)-1) {
1208 #ifdef COMMENT
1209 	sprintf((char *) &data[i+2],"%ld",x.length); /* safe */
1210 #else
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) {
1215 	    data[i] = c;
1216 	    data[i+1] = tochar(aln);
1217 	    i += aln + 2;
1218 	    numset++;
1219 	    done[xunchar(c)] = 1;
1220 	} else {
1221 	    data[i] = NUL;
1222 	    left++;
1223 	}
1224     }
1225 #ifdef CK_PERMS
1226     if (atlpro && !done[xunchar(c = ',')] && /* Local protection */
1227 	(aln = x.lprotect.len) > 0 && !notafile && xp == 0) {
1228 	if (max - i >= aln + 2) {
1229 	    data[i++] = c;
1230 	    data[i++] = tochar(aln);
1231 	    for (j = 0; j < aln; j++)
1232 	      data[i++] = x.lprotect.val[j];
1233 	    numset++;
1234 	    done[xunchar(c)] = 1;
1235 	} else
1236 	  left++;
1237     }
1238     if (atgpro && !done[xunchar(c = '-')] && /* Generic protection */
1239 	(aln = x.gprotect.len) > 0 && !notafile && xp == 0) {
1240 	if (max - i >= aln + 2) {
1241 	    data[i++] = c;
1242 	    data[i++] = tochar(aln);
1243 	    for (j = 0; j < aln; j++)
1244 	      data[i++] = x.gprotect.val[j];
1245 	    numset++;
1246 	    done[xunchar(c)] = 1;
1247 	} else
1248 	  left++;
1249     }
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) {
1256 	    data[i] = c;
1257 	    data[i+1] = tochar(aln);
1258 	    i += aln + 2;
1259 	    numset++;
1260 	    done[xunchar(c)] = 1;
1261 	} else {
1262 	    data[i] = NUL;
1263 	    left++;
1264 	}
1265     }
1266 #ifndef NOFRILLS
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 */
1273 	    if (rprintf)
1274 	      data[i++] = 'P';		/* P for Print */
1275 	    else
1276 	      data[i++] = 'M';		/* M for Mail */
1277 	    for (j = 0; optbuf[j]; j++)	/* Copy any options */
1278 	      data[i++] = optbuf[j];
1279 	    numset++;
1280 	    done[xunchar(c)] = 1;
1281 	} else {
1282 	    data[i] = NUL;
1283 	    left++;
1284 	}
1285     }
1286 #endif /* NOFRILLS */
1287 #ifdef CK_RESEND
1288     if (sendmode == SM_RESEND && !done[xunchar(c = '+')]) {
1289 	if (max - i >= 3) {
1290 	    data[i++] = c;		/* Disposition */
1291 	    data[i++] = tochar(1);
1292 	    data[i++] = 'R';		/* is RESEND */
1293 	    numset++;
1294 	    done[xunchar(c)] = 1;
1295 	} else
1296 	  left++;
1297     }
1298 #endif /* CK_RESEND */
1299 
1300     /* End of Attributes -- to be sent only after sending all others */
1301 
1302     debug(F111,"sattr","@",i);
1303     debug(F101,"sattr numset","",numset);
1304     debug(F101,"sattr left","",left);
1305 
1306     if ((left == 0 || numset == 0) && !done[xunchar(c = '@')]) {
1307 	if (max - i >= 3) {
1308 	    data[i++] = c;		/* End of Attributes */
1309 	    data[i++] = SP;		/* Length 0 */
1310 	    data[i] = NUL;		/* Make sure it's null-terminated */
1311 	    numset++;
1312 	    done[xunchar(c)] = 1;
1313 	}
1314     }
1315 
1316     /* Finished - send the packet off if we have anything in it */
1317 
1318     if (numset) {
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 */
1323     } else
1324       return(0);
1325 }
1326 
1327 static char *refused = "";
1328 
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 */
1334     "size",				/* 1 */
1335     "2",				/* 2 */
1336     "3",				/* 3 */
1337     "4",				/* 4 */
1338     "5",				/* 5 */
1339     "6",				/* 6 */
1340     "7",				/* 7 */
1341     "8",				/* 8 */
1342     "9",				/* 9 */
1343     ":",				/* : */
1344     ";",				/* ; */
1345     "<",				/* < */
1346     "=",				/* = */
1347     ">",				/* > */
1348     "name",				/* ? */
1349     "@"
1350 };
1351 static int nreason = sizeof(reason) / sizeof(char *);
1352 int rejection = -1;
1353 
1354 char *
getreason(s)1355 getreason(s) char *s; {			/* Decode attribute refusal reason */
1356     char c, *p;
1357     if (rejection == 1)			/* Kludge for SET FIL COLL DISCARD */
1358       return("name");			/* when other Kermit doesn't... */
1359     p = s;
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) ?
1365 	  reason[c] :
1366 	    "unknown";
1367     }
1368     return(p);
1369 }
1370 
1371 int
rsattr(s)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);
1378 	return(-1);
1379     }
1380 #ifdef CK_RESEND
1381     if (sendmode == SM_RESEND && *s == '1') { /* RESEND length */
1382 	int n; CK_OFF_T z; CHAR *p;
1383 	p = s + 1;
1384 	n = xunchar(*p++);
1385 	debug(F101,"rsattr RESEND n","",n);
1386 	z = (CK_OFF_T)0;
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. */
1394 	    return(0);
1395 #ifdef CK_CURSES
1396 	if (fdispla == XYFD_C)
1397 	  xxscreen(SCR_FS,0,fsize,"");	/* Refresh file transfer display */
1398 #endif /* CK_CURSES */
1399     }
1400 #endif /* CK_RESEND */
1401     refused = "";
1402     return(0);
1403 }
1404 
1405 /*
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
1409 */
1410 int
gattr(s,yy)1411 gattr(s, yy) CHAR *s; struct zattr *yy; { /* Read incoming attribute packet */
1412     char c, d;
1413     char *ff;
1414     int aln, i, overflow = 0;
1415 
1416 #ifndef NOCSETS
1417     extern int r_cset, axcset[];
1418 #endif /* NOCSETS */
1419 
1420 #define ABUFL 40			/* Temporary buffer for conversions */
1421     char abuf[ABUFL+1];
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];
1432 #ifndef DYNAMIC
1433 #define DSBUFL 100			/* Disposition */
1434     static char dsbuf[DSBUFL+1];
1435 #define SPBUFL 512			/* System-dependent parameters */
1436     static char spbuf[SPBUFL+1];
1437 #else
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];
1445 
1446 #ifdef CK_PERMS
1447     static char lprmbuf[CK_PERMLEN+1];
1448     static char gprmbuf[2];
1449 #endif /* CK_PERMS */
1450 
1451     char *rp;				/* Pointer to reply buffer */
1452     int retcode;			/* Return code */
1453 
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 */
1458 
1459 /* Fill in the attributes we have received */
1460 
1461     rp = rpbuf;				/* Initialize reply buffer */
1462     *rp++ = 'N';			/* for negative reply. */
1463     *rp = NUL;
1464     retcode = 0;			/* Initialize return code. */
1465 
1466     if (dest == DEST_P) {		/* SET DESTINATION PRINTER */
1467 #ifdef DYNAMIC
1468 	if (!dsbuf)
1469 	  if ((dsbuf = malloc(DSBUFL+1)) == NULL)
1470 	    fatal("gtattr: no memory for dsbuf");
1471 #endif /* DYNAMIC */
1472 	dsbuf[0] = 'P';
1473 	dsbuf[1] = '\0';
1474 	yy->disp.val = dsbuf;
1475 	yy->disp.len = 1;
1476     }
1477     while (c = *s++) {			/* Get attribute tag */
1478 	aln = xunchar(*s++);		/* Length of attribute string */
1479 	switch (c) {
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 */
1483 	      abuf[i] = *s++;
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 */
1487 	    break;
1488 #endif	/* COMMENT */
1489 
1490 	  case '/':			/* Record format */
1491 	    rfbuf[1] = NUL;
1492 	    rfbuf[2] = NUL;
1493 	    for (i = 0; (i < aln) && (i < RFBUFL); i++) /* Copy it */
1494 	      rfbuf[i] = *s++;
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);
1502 		*rp++ = c;
1503 		retcode = -1;
1504 	    }
1505 	    break;
1506 
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 */
1510 	    ftbuf[i] = '\0';
1511 	    if (i < aln) s += (aln - i);
1512 	    /* TYPE attribute is enabled? */
1513 	    if (attypi) {
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);
1518 		/* Unknown type? */
1519 		if ((*ftbuf != 'A' && *ftbuf != 'B' && *ftbuf != 'I')
1520 #ifdef CK_LABELED
1521 /* ... Or our FILE TYPE is LABELED and the incoming file is text... */
1522 		    || (binary == XYFT_L && *ftbuf == 'A' && !xflg)
1523 #endif /* CK_LABELED */
1524 		    ) {
1525 		    retcode = -1;	/* Reject the file */
1526 		    *rp++ = c;
1527 		    if (!opnerr) tlog(F100," refused: type","",0);
1528 		    break;
1529 		}
1530 /*
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.
1535 */
1536 		if (yy->type.val[0] == 'A') { /* Check received attributes. */
1537 #ifdef VMS
1538 		    if (binary != XYFT_I) /* VMS IMAGE overrides this */
1539 #endif /* VMS */
1540 		      binary = XYFT_T;	/* Set current type to Text. */
1541 		    debug(F101,"gattr binary 2","",binary);
1542 		} else if (yy->type.val[0] == 'B') {
1543 #ifdef CK_LABELED
1544 		    if (binary != XYFT_L
1545 #ifdef VMS
1546 			&& binary != XYFT_U /* VMS special case */
1547 #endif /* VMS */
1548 			)
1549 #endif /* CK_LABELED */
1550 #ifdef MAC
1551 		    if (binary != XYFT_M) /* If not MacBinary... */
1552 #endif /* MAC */
1553 		      binary = XYFT_B;
1554 		    debug(F101,"gattr binary 3","",binary);
1555 		}
1556 	    }
1557 	    break;
1558 
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);
1563 	    dtbuf[i] = '\0';
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. */
1572 		    }
1573 		}
1574 	    }
1575 	    break;
1576 
1577 	  case '(':			/* File Block Size */
1578 	    for (i = 0; (i < aln) && (i < ABUFL); i++) /* Copy it */
1579 	      abuf[i] = *s++;
1580 	    abuf[i] = '\0';		/* Terminate with null */
1581 	    if (i < aln) s += (aln - i);
1582 	    if (atblki)
1583 	      yy->blksize = atol(abuf); /* Convert to number */
1584 	    break;
1585 
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);
1590 	    tsbuf[i] = '\0';
1591 #ifndef NOCSETS
1592 	    xlatype = XLA_NONE;		/* Assume no translation */
1593 #endif /* NOCSETS */
1594 	    if (atenci) {
1595 		char * ss;
1596 		yy->encoding.val = tsbuf; /* Pointer to string */
1597 		yy->encoding.len = i;	/* Length of string */
1598 		debug(F101,"gattr encoding",tsbuf,i);
1599 		ss = tsbuf+1;
1600 		switch (*tsbuf) {
1601 #ifndef NOCSETS
1602 		  case 'A':		  /* Normal, nothing special */
1603 		    tcharset = TC_TRANSP; /* Transparent chars untranslated */
1604 		    debug(F110,"gattr sets tcharset TC_TRANSP","A",0);
1605 		    break;
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);
1610 			break;
1611 		    }
1612 #ifdef UNICODE
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))
1620 			  break;
1621 		    }
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. */
1627 			    *rp++ = c;
1628 			    if (!opnerr)
1629 			      tlog(F100," refused: character set","",0);
1630 			}
1631 		    } else {
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) {
1637 				int x;
1638 				x = axcset[tcharset];
1639 				if (x > 0 && x <= MAXFCSETS) {
1640 				    fcharset = x;
1641 				    debug(F101,"gattr switch fcharset","",x);
1642 				}
1643 			    }
1644 			}
1645 			/* Set up translation type and function */
1646 			setxlatype(tcharset,fcharset);
1647 		    }
1648 		break;
1649 #endif /* NOCSETS */
1650 	      default:			/* Something else. */
1651 		debug(F110,"gattr unk encoding attribute",tsbuf,0);
1652 		if (!unkcs) {		/* If SET UNK DISC */
1653 		    retcode = -1;
1654 		    *rp++ = c;
1655 		    if (!opnerr) tlog(F100," refused: encoding","",0);
1656 		}
1657 		break;
1658 		}
1659 	    }
1660 	    break;
1661 
1662 	  case '+':			/* Disposition */
1663 #ifdef DYNAMIC
1664 	    if (!dsbuf)
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 */
1670 	    dsbuf[i] = '\0';
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 */
1677 		d = *dsbuf;
1678 #ifndef NODISPO
1679 /*
1680   Define NODISPO to disable receipt of mail or print files and of RESEND.
1681 */
1682 		if (
1683 #ifndef datageneral			/* MAIL supported only for */
1684 #ifndef OS2				/* UNIX, VMS, and OS-9 */
1685 #ifndef MAC
1686 #ifndef GEMDOS
1687 #ifndef AMIGA
1688 		    d != 'M' &&		/* MAIL */
1689 #endif /* AMIGA */
1690 #endif /* GEMDOS */
1691 #endif /* MAC */
1692 #endif /* OS/2 */
1693 #endif /* datageneral */
1694 #ifdef CK_RESEND
1695 		    d != 'R' &&		/* RESEND */
1696 #endif /* CK_RESEND */
1697 		    d != 'P') {		/* PRINT */
1698 		    retcode = -1;	/* Unknown/unsupported disposition */
1699 		    *rp++ = c;
1700 		    if (!opnerr) tlog(F101," refused: bad disposition","",d);
1701 		}
1702 		dispos = d;
1703 		debug(F000,"gattr dispos","",dispos);
1704 		switch (d) {
1705 #ifndef NOFRILLS
1706 		  case 'M':
1707 		    if (!en_mai) {
1708 			retcode = -1;
1709 			*rp++ = c;
1710 			if (!opnerr) tlog(F100," refused: mail disabled","",0);
1711 			dispos = 0;
1712 		    }
1713 		    break;
1714 #endif /* NOFRILLS */
1715 		  case 'P':
1716 		    if (!en_pri) {
1717 			retcode = -1;
1718 			*rp++ = c;
1719 			if (!opnerr)
1720 			  tlog(F100," refused: print disabled","",0);
1721 			dispos = 0;
1722 		    }
1723 		    break;
1724 
1725 		  case 'R':
1726 		    dispos = 0;
1727 #ifdef CK_RESEND
1728 		    rs_len = zgetfs(ff); /* Get length of file */
1729 		    debug(F111,"gattr RESEND",ff,rs_len);
1730 #ifdef VMS
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);
1734 #endif /* VMS */
1735 #ifdef COMMENT
1736 		    if (rs_len < 0L)	/* Local file doesn't exist */
1737 		      rs_len = 0L;
1738 #endif /* COMMENT */
1739 /*
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.
1748 */
1749 		    if (rs_len > (CK_OFF_T)0) {
1750 			fncsav = fncact; /* Save collision action */
1751 			fncact = XYFX_A; /* Switch to APPEND */
1752 		    }
1753 #else
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 */
1758 		}
1759 #else  /* NODISPO */
1760 		retcode = -1;
1761 		*rp++ = c;
1762 		if (!opnerr) tlog(F100," refused: NODISPO","",0);
1763 #endif /* NODISPO */
1764 	    }
1765 	    break;
1766 
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 */
1770 	    idbuf[i] = '\0';
1771 	    if (i < aln) s += (aln - i);
1772 	    if (atsidi) {
1773 		yy->systemid.val = idbuf; /* Pointer to string */
1774 		yy->systemid.len = i;	/* Length of string */
1775 	    }
1776 	    break;
1777 
1778 	  case '0':			/* System-dependent parameters */
1779 #ifdef DYNAMIC
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 */
1785 	    spbuf[i] = '\0';
1786 	    if (i < aln) s += (aln - i);
1787 	    if (atsysi) {
1788 		yy->sysparam.val = spbuf; /* Pointer to string */
1789 		yy->sysparam.len = i;	/* Length of string */
1790 	    }
1791 	    break;
1792 
1793 	  case '!':			/* File length in K */
1794 	  case '1': {			/* File length in bytes */
1795 	      char * l2;
1796 	      CK_OFF_T xlen;
1797 	      for (i = 0; (i < aln) && (i < ABUFL); i++) /* Copy it */
1798 		abuf[i] = *s++;
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 */
1804 		  if (c == '1')
1805 		    debug(F111,"gattr length",abuf,xlen);
1806 		  else
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 */
1810 		      overflow++;
1811 		      debug(F111,"gattr overflow",
1812 			    (c == '1') ? "length" : "lengthk",
1813 			    xlen);
1814 		  }
1815 		  if (c == '1') {
1816 		      yy->length = xlen;
1817 		      debug(F101,"gattr length","",xlen);
1818 		  } else {
1819 		      yy->lengthk = xlen;
1820 		      debug(F101,"gattr lengthk","",xlen);
1821 		  }
1822 	      }
1823 	      /* If the length field is not numeric accept the file */
1824 	      /* anyway but with an unknown length */
1825 	      break;
1826 	  }
1827 
1828 #ifdef CK_PERMS
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);
1834 	    if (atlpri) {
1835 		yy->lprotect.val = (char *)lprmbuf;
1836 		yy->lprotect.len = i;
1837 	    } else
1838 	      lprmbuf[0] = NUL;
1839 	    break;
1840 
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++;
1845 	    gprmbuf[1] = NUL;
1846 	    if (atgpri) {
1847 		yy->gprotect.val = (char *)gprmbuf;
1848 		yy->gprotect.len = gprmbuf[0] ? 1 : 0;
1849 	    } else
1850 	      gprmbuf[0] = NUL;
1851 	    break;
1852 #endif /* CK_PERMS */
1853 
1854 	  default:			/* Unknown attribute */
1855 	    s += aln;			/* Just skip past it */
1856 	    break;
1857 	}
1858     }
1859 
1860     /* Check space now, because we also need to know the file type */
1861     /* in case zchkspa() differentiates text and binary (VMS version does) */
1862 
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 */
1867 		*rp++ = '1';
1868 		if (!opnerr) tlog(F100," refused: length bytes","",0);
1869 	    }
1870 	} else if (yy->lengthk > (CK_OFF_T)-1) { /* Length in K received? */
1871 	    long xlen;
1872 	    xlen = yy->lengthk * 1024;
1873 	    if (!zchkspa(ff,xlen)) {
1874 		retcode = -1;		/* Check space */
1875 		*rp++ = '!';
1876 		if (!opnerr) tlog(F100," refused: length K","",0);
1877 	    }
1878 	}
1879     }
1880     if (retcode > -1L) {		/* Remember the file size */
1881 	if (yy->length > (CK_OFF_T)-1) {
1882 	    fsize = yy->length;
1883 	} else if (yy->lengthk > (CK_OFF_T)-1 && !overflow) {
1884 	    fsize = yy->lengthk * 1024L;
1885 	} else fsize = yy->length;	/* (e.g. -2L) */
1886     }
1887 
1888 #ifdef DEBUG
1889     if (deblog) {
1890 #ifdef COMMENT
1891 	sprintf(abuf,"%ld",fsize);	/* safe */
1892 #else
1893 	ckstrncpy(abuf,ckfstoa(fsize),ABUFL);
1894 #endif	/* COMMENT */
1895 debug(F110,"gattr fsize",abuf,0);
1896     }
1897 #endif /* DEBUG */
1898 
1899     if (retcode == 0) rp = rpbuf;	/* Null reply string if accepted */
1900     *rp = '\0';				/* End of reply string */
1901 
1902 #ifdef CK_RESEND
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 */
1913 #ifdef COMMENT
1914 	    sprintf(rpbuf+2,"%ld",rs_len); /* Reply with length of file */
1915 #else
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);
1921 	}
1922     }
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) */
1927     }
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. */
1933 	if (fncsav != -1) {
1934 	    fncact = fncsav;
1935 	    fncsav = -1;
1936 	}
1937     }
1938     debug(F111,"gattr return",rpbuf,retcode);
1939     return(retcode);
1940 }
1941 
1942 /*  I N I T A T T R  --  Initialize file attribute structure  */
1943 
1944 int
initattr(yy)1945 initattr(yy) struct zattr *yy; {
1946     yy->lengthk = yy->length = (CK_OFF_T)-1;
1947     yy->type.val = "";
1948     yy->type.len = 0;
1949     yy->date.val = "";
1950     yy->date.len = 0;
1951     yy->encoding.val = "";
1952     yy->encoding.len = 0;
1953     yy->disp.val = "";
1954     yy->disp.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;
1963     yy->area.val = "";
1964     yy->area.len = 0;
1965     yy->password.val = "";
1966     yy->password.len = 0;
1967     yy->blksize = -1L;
1968     yy->xaccess.val = "";
1969     yy->xaccess.len = 0;
1970 #ifdef CK_PERMS
1971     if (!ofperms) ofperms = "";
1972     debug(F110,"initattr ofperms",ofperms,0);
1973     yy->lprotect.val = ofperms;
1974     yy->lprotect.len = 0 - strlen(ofperms); /* <-- NOTE! */
1975     /*
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.
1979     */
1980 #else
1981     yy->lprotect.val = "";
1982     yy->lprotect.len = 0;
1983 #endif /* CK_PERMS */
1984     yy->gprotect.val = "";
1985     yy->gprotect.len = 0;
1986     yy->recfm.val = "";
1987     yy->recfm.len = 0;
1988     yy->reply.val = "";
1989     yy->reply.len = 0;
1990 #ifdef OS2
1991     yy->longname.len = 0 ;
1992     yy->longname.val = "" ;
1993 #endif /* OS2 */
1994     return(0);
1995 }
1996 
1997 /*  A D E B U -- Write attribute packet info to debug log  */
1998 
1999 int
adebu(f,zz)2000 adebu(f,zz) char *f; struct zattr *zz; {
2001 #ifdef DEBUG
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);
2022 #endif /* DEBUG */
2023     return(0);
2024 }
2025 
2026 /*  O P E N A -- Open a file, with attributes.  */
2027 /*
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).
2034 
2035   Returns 0 on failure, nonzero on success.
2036 */
2037 extern char *rf_err;
2038 
2039 int
opena(f,zz)2040 opena(f,zz) char *f; struct zattr *zz; {
2041     int x, dispos = 0;
2042     static struct filinfo fcb;		/* Must be static! */
2043 
2044     debug(F110,"opena f",f,0);
2045     debug(F101,"opena discard","",discard);
2046 
2047     adebu(f,zz);			/* Write attributes to debug log */
2048 
2049     ffc = (CK_OFF_T)0;			/* Init file-character counter */
2050 
2051 #ifdef PIPESEND
2052     if (pipesend)			/* Receiving to a pipe - easy. */
2053       return(openo(f,zz,&fcb));		/* Just open the pipe. */
2054 #endif /* PIPESEND */
2055 
2056     /* Receiving to a file - set up file control structure */
2057 
2058     fcb.bs = fblksiz;			/* Blocksize */
2059 #ifndef NOCSETS
2060     fcb.cs = fcharset;			/* Character set */
2061 #else
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 >> */
2073       fcb.dsp = XYFZ_A;
2074     else
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 */
2078 #ifdef CK_LABELED
2079     fcb.lblopts = lf_opts;		/* Labeled file options */
2080 #else
2081     fcb.lblopts = 0;
2082 #endif /* CK_LABELED */
2083 
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));
2087     }
2088     if (!dispos && xflg && remfile && remappd) /* REMOTE redirect append ? */
2089       dispos = fcb.dsp;
2090 
2091     debug(F101,"opena dispos","",dispos);
2092 
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";
2098 		return(0);
2099 	    } else debug(F110,"opena rename ok",ofn2,0);
2100 	}
2101     } else if (dispos == 'R') {		/* Receiving a RESEND */
2102 	debug(F101,"opena remote len","",zz->length);
2103 	debug(F101,"opena local len","",rs_len);
2104 #ifdef COMMENT
2105         if (fncact == XYFX_R)		/* and file collision = RENAME */
2106 	  if (ofn1x)
2107 #endif /* COMMENT */
2108 	if (ofn1[0])
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 */
2115     }
2116     debug(F111,"opena [file]=mode: ",f,fcb.dsp);
2117     if (x = openo(f,zz,&fcb)) {		/* Try to open the file. */
2118 #ifdef pdp11
2119 	tlog(F110," local name:",f,0L);	/* OK, open, record local name. */
2120 	makestr(&prfspec,f);		/* New preliminary name */
2121 #else
2122 #ifndef ZFNQFP
2123 	tlog(F110," local name:",f,0L);
2124 	makestr(&prfspec,f);
2125 #else
2126 	{				/* Log full local pathname */
2127 	    char *p = NULL, *q = f;
2128 	    if ((p = malloc(CKMAXPATH+1)))
2129 	      if (zfnqfp(filnam, CKMAXPATH, p))
2130 		q = p;
2131 	    tlog(F110," local name:",q,0L);
2132 	    makestr(&prfspec,q);
2133 	    if (p) free(p);
2134 	}
2135 #endif /* ZFNQFP */
2136 #endif /* pdp11 */
2137 
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);
2142 #ifndef NOCSETS
2143 	    if (xfrxla) {
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);
2148 	    } else {
2149 		  tlog(F110," character-set:","transparent",0L);
2150 	    }
2151 #endif /* NOCSETS */
2152 	    debug(F111,"opena charset",zz->encoding.val,zz->encoding.len);
2153 	}
2154 	debug(F101,"opena binary","",binary);
2155 
2156 #ifdef COMMENT
2157 	if (fsize >= 0)
2158 #endif /* COMMENT */
2159 	  xxscreen(SCR_FS,0,fsize,"");
2160 
2161 #ifdef datageneral
2162 /*
2163   Need to turn on multi-tasking console interrupt task here, since multiple
2164   files may be received (huh?) ...
2165 */
2166         if ((local) && (!quiet))        /* Only do this if local & not quiet */
2167 	  consta_mt();			/* Start the async read task */
2168 #endif /* datageneral */
2169 
2170     } else {				/* Did not open file OK. */
2171 
2172 	rf_err = ck_errstr();		/* Get system error message */
2173 	if (*rf_err)
2174 	  xxscreen(SCR_EM,0,0l,rf_err);
2175 	else
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);
2180     }
2181     return(x);				/* Pass on return code from openo */
2182 }
2183 
2184 /*  O P E N C  --  Open a command (in place of a file) for output */
2185 
2186 int
openc(n,s)2187 openc(n,s) int n; char * s; {
2188     int x;
2189 #ifndef NOPUSH
2190     x = zxcmd(n,s);
2191 #else
2192     x = 0;
2193 #endif /* NOPUSH */
2194     debug(F111,"openc zxcmd",s,x);
2195     o_isopen = (x > 0) ? 1 : 0;
2196     return(x);
2197 }
2198 
2199 /*  C A N N E D  --  Check if current file transfer cancelled */
2200 
2201 int
canned(buf)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)
2207       interrupted = 1;
2208     debug(F101,"canned: cxseen","",cxseen);
2209     debug(F101," czseen","",czseen);
2210     return((czseen || cxseen) ? 1 : 0);
2211 }
2212 
2213 
2214 /*  O P E N I  --  Open an existing file for input  */
2215 
2216 int
openi(name)2217 openi(name) char *name; {
2218 #ifndef NOSERVER
2219     extern int fromgetpath;
2220 #endif /* NOSERVER */
2221     int x, filno;
2222     char *name2;
2223     extern CHAR *epktmsg;
2224 
2225     epktmsg[0] = NUL;			/* Initialize error message */
2226     if (memstr || sndarray) {		/* Just return if "file" is memory. */
2227 	i_isopen = 1;
2228 	return(1);
2229     }
2230     debug(F110,"openi name",name,0);
2231     debug(F101,"openi sndsrc","",sndsrc);
2232 
2233     filno = (sndsrc == 0) ? ZSTDIO : ZIFILE;    /* ... */
2234     debug(F101,"openi file number","",filno);
2235 
2236 #ifndef NOSERVER
2237     /* If I'm a server and CWD is disabled and name is not from GET-PATH... */
2238 
2239     if (server && !en_cwd && !fromgetpath) {
2240 	zstrip(name,&name2);
2241 	if (				/* ... check if pathname included. */
2242 #ifdef VMS
2243 	    zchkpath(name)
2244 #else
2245 	    strcmp(name,name2)
2246 #endif /* VMS */
2247 	    ) {
2248 	    tlog(F110,name,"access denied",0L);
2249 	    debug(F110,"openi CD disabled",name,0);
2250 	    ckstrncpy((char *)epktmsg,"Access denied",PKTMSGLEN);
2251 	    return(0);
2252 	} else name = name2;
2253     }
2254 #endif /* NOSERVER */
2255 
2256 #ifdef PIPESEND
2257     debug(F101,"openi pipesend","",pipesend);
2258     if (pipesend) {
2259 	int x;
2260 #ifndef NOPUSH
2261 	x = zxcmd(ZIFILE,name);
2262 #else
2263 	x = 0;
2264 #endif /* NOPUSH */
2265 	i_isopen = (x > 0) ? 1 : 0;
2266 	if (!i_isopen)
2267 	  ckstrncpy((char *)epktmsg,"Command or pipe failure",PKTMSGLEN);
2268 	debug(F111,"openi pipesend zxcmd",name,x);
2269 	return(i_isopen);
2270     }
2271 #endif /* PIPESEND */
2272 
2273 #ifdef CALIBRATE
2274     if (calibrate) {
2275 	i_isopen = 1;
2276     	return(1);
2277     }
2278 #endif /* CALIBRATE */
2279 
2280     x = zopeni(filno,name);		/* Otherwise, try to open it. */
2281     debug(F111,"openi zopeni 1",name,x);
2282     if (x) {
2283 	i_isopen = 1;
2284     	return(1);
2285     } else {				/* If not found, */
2286 	char xname[CKMAXPATH];		/* convert the name */
2287 #ifdef NZLTOR
2288 	nzrtol(name,xname,fncnv,fnrpath,CKMAXPATH);
2289 #else
2290 	zrtol(name,xname);		/* to local form and then */
2291 #endif /*  NZLTOR */
2292 	x = zopeni(filno,xname);	/* try opening it again. */
2293 	debug(F111,"openi zopeni 2",xname,x);
2294 	if (x) {
2295 	    i_isopen = 1;
2296 	    return(1);			/* It worked. */
2297         } else {
2298 	    char * s;
2299 	    s = ck_errstr();
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);
2306 	    i_isopen = 0;
2307 	    return(0);
2308         }
2309     }
2310 }
2311 
2312 /*  O P E N O  --  Open a new file for output.  */
2313 
2314 int
openo(name,zz,fcb)2315 openo(name,zz,fcb) char *name; struct zattr *zz; struct filinfo *fcb; {
2316     char *name2;
2317 #ifdef DTILDE
2318     char *dirp;
2319 #endif /* DTILDE */
2320 
2321     int channel, x;
2322 
2323     if (stdouf) {				/* Receiving to stdout? */
2324 	x = zopeno(ZSTDIO,"",zz,NULL);
2325 	o_isopen = (x > 0);
2326 	debug(F101,"openo stdouf zopeno","",x);
2327 	return(x);
2328     }
2329     debug(F110,"openo: name",name,0);
2330 
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. */
2334     }
2335     channel = ZOFILE;			/* SET DESTINATION DISK or PRINTER */
2336 
2337 #ifdef PIPESEND
2338     debug(F101,"openo pipesend","",pipesend);
2339     if (pipesend) {
2340 	int x;
2341 #ifndef NOPUSH
2342 	x = zxcmd(ZOFILE,(char *)srvcmd);
2343 #else
2344 	x = 0;
2345 #endif /* NOPUSH */
2346 	o_isopen = x > 0;
2347 	debug(F101,"openo zxcmd","",x);
2348 	return(x);
2349     }
2350 #endif /* PIPESEND */
2351 
2352     if (dest == DEST_S) {		/* SET DEST SCREEN... */
2353 	channel = ZCTERM;
2354 	fcb = NULL;
2355     }
2356 #ifdef DTILDE
2357     if (*name == '~') {
2358 	dirp = tilde_expand(name);
2359 	if (*dirp) ckstrncpy(name,dirp,CKMAXPATH+1);
2360     }
2361 #endif /* DTILDE */
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);
2367 	    return(0);
2368 	} else name = name2;
2369     }
2370     if (zopeno(channel,name,zz,fcb) <= 0) { /* Try to open the file */
2371 	o_isopen = 0;
2372 	debug(F110,"openo failed",name,0);
2373 	/* tlog(F110,"Failure to open",name,0L); */
2374 	return(0);
2375     } else {
2376 	o_isopen = 1;
2377 	debug(F110,"openo ok, name",name,0);
2378 	return(1);
2379     }
2380 }
2381 
2382 /*  O P E N T  --  Open the terminal for output, in place of a file  */
2383 
2384 int
opent(zz)2385 opent(zz) struct zattr *zz; {
2386     int x;
2387     ffc = tfc = (CK_OFF_T)0;
2388     x = zopeno(ZCTERM,"",zz,NULL);
2389     debug(F101,"opent zopeno","",x);
2390     if (x >= 0) {
2391 	o_isopen = 1;
2392 	binary = XYFT_T;
2393     } else
2394       return(0);
2395     return(x);
2396 }
2397 
2398 /*  O P E N X  --  Open nothing (incoming file to be accepted but ignored)  */
2399 
2400 int
ckopenx(zz)2401 ckopenx(zz) struct zattr *zz; {
2402     ffc = tfc = (CK_OFF_T)0;		/* Reset counters */
2403     o_isopen = 1;
2404     debug(F101,"ckopenx fsize","",fsize);
2405     xxscreen(SCR_FS,0,fsize,"");	/* Let screen display know the size */
2406     return(1);
2407 }
2408 
2409 /*  C L S I F  --  Close the current input file. */
2410 
2411 int
clsif()2412 clsif() {
2413     extern int xferstat, success;
2414     int x = 0;
2415 
2416     fcps();			/* Calculate CPS quickly */
2417 
2418 #ifdef datageneral
2419     if ((local) && (!quiet))    /* Only do this if local & not quiet */
2420       if (nfils < 1)		/* More files to send ... leave it on! */
2421 	connoi_mt();
2422 #endif /* datageneral */
2423 
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. */
2428 	} else {
2429 	    x = zclose(ZIFILE);		/* else close input file. */
2430 	}
2431 #ifdef DEBUG
2432 	if (deblog) {
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);
2441 	}
2442 #endif /* DEBUG */
2443 	if ((cxseen || czseen) && !epktsent) { /* If interrupted */
2444 	    xxscreen(SCR_ST,ST_INT,0l,""); /* say so */
2445 #ifdef TLOG
2446 	    if (tralog && !tlogfmt)
2447 	      doxlog(what,psfspec,fsize,binary,1,"Interrupted");
2448 #endif /* TLOG */
2449 	} else if (discard && !epktsent) { /* If I'm refusing */
2450 	    xxscreen(SCR_ST,ST_REFU,0l,refused); /* say why */
2451 #ifdef TLOG
2452 	    if (tralog && !tlogfmt) {
2453 		char buf[128];
2454 		ckmakmsg(buf,128,"Refused: ",refused,NULL,NULL);
2455 		doxlog(what,psfspec,fsize,binary,1,buf);
2456 	    }
2457 #endif /* TLOG */
2458 	} else if (!epktrcvd && !epktsent && !cxseen && !czseen) {
2459 	    CK_OFF_T zz;
2460 	    zz = ffc;
2461 #ifdef CK_RESEND
2462 	    if (sendmode == SM_RESEND || sendmode == SM_PSEND)
2463 	      zz += sendstart;
2464 #endif /* CK_RESEND */
2465 	    debug(F101,"clsif fstats","",zz);
2466 	    fstats();			/* Update statistics */
2467 	    if (			/* Was the whole file sent? */
2468 #ifdef VMS
2469 		0			/* Not a reliable check in VMS */
2470 #else
2471 #ifdef STRATUS
2472 		0			/* Probably not for VOS either */
2473 #else
2474 		zz < fsize
2475 #ifdef CK_CTRLZ
2476 		&& ((eofmethod != XYEOF_Z && !binary) || binary)
2477 #endif /* CK_CTRLZ */
2478 #endif /* STRATUS */
2479 #endif /* VMS */
2480 		) {
2481 		xxscreen(SCR_ST,ST_INT,0l,"");
2482 #ifdef TLOG
2483 		if (tralog && !tlogfmt)
2484 		  doxlog(what,psfspec,fsize,binary,1,"Incomplete");
2485 #endif /* TLOG */
2486 	    } else {
2487 #ifdef COMMENT
2488 		/* Not yet -- we don't have confirmation from the receiver */
2489 		xxscreen(SCR_ST,ST_OK,0l,"");
2490 #endif /* COMMENT */
2491 #ifdef TLOG
2492 		if (tralog && !tlogfmt)
2493 		  doxlog(what,psfspec,fsize,binary,0,"");
2494 #endif /* TLOG */
2495 	    }
2496 	}
2497     }
2498     i_isopen = 0;
2499     hcflg = 0;				/* Reset flags */
2500     sendstart = (CK_OFF_T)0;		/* Don't do this again! */
2501 #ifdef COMMENT
2502 /*
2503   This prevents a subsequent call to clsof() from deleting the file
2504   when given the discard flag.
2505 */
2506     *filnam = '\0';			/* and current file name */
2507 #endif /* COMMENT */
2508     return(x);
2509 }
2510 
2511 
2512 /*  C L S O F  --  Close an output file.  */
2513 
2514 /*  Call with disp != 0 if file is to be discarded.  */
2515 /*  Returns -1 upon failure to close, 0 or greater on success. */
2516 
2517 int
clsof(disp)2518 clsof(disp) int disp; {
2519     int x = 0;
2520     extern int success;
2521 
2522     fcps();				/* Calculate CPS quickly */
2523 
2524     debug(F101,"clsof disp","",disp);
2525     debug(F101,"clsof cxseen","",cxseen);
2526     debug(F101,"clsof success","",success);
2527 
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. */
2532     }
2533 #ifdef datageneral
2534     if ((local) && (!quiet))		/* Only do this if local & not quiet */
2535       connoi_mt();
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");
2541 #ifdef TLOG
2542 	    if (tralog && !tlogfmt)
2543 	      doxlog(what,prfspec,fsize,binary,1,"Can't close file");
2544 #endif /* TLOG */
2545 	} else if (disp) {		/* Interrupted or refused */
2546 	    if (keep == 0 ||		/* If not keeping incomplete files */
2547 		(keep == SET_AUTO && binary == XYFT_T)
2548 		) {
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,"");
2556 #ifdef TLOG
2557 			if (tralog && !tlogfmt)
2558 			  doxlog(what,prfspec,fsize,binary,1,"Discarded");
2559 #endif /* TLOG */
2560 		    }
2561 		}
2562 	    } else {			/* Keep incomplete copy */
2563 		debug(F100,"clsof fstats 1","",0);
2564 		fstats();
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);
2569 		    }
2570 		}
2571 		if (what & W_KERMIT) {
2572 		    if (!epktrcvd && !epktsent) {
2573 			xxscreen(SCR_ST,ST_INC,0l,"");
2574 #ifdef TLOG
2575 			if (tralog && !tlogfmt)
2576 			  doxlog(what,prfspec,fsize,binary,1,"Incomplete");
2577 #endif /* TLOG */
2578 		    }
2579 		}
2580 	    }
2581 	}
2582     }
2583     if (o_isopen && x > -1 && !disp) {
2584 	debug(F110,"clsof OK",rfspec,0);
2585 	makestr(&rfspec,prfspec);
2586 	makestr(&rrfspec,prrfspec);
2587 	fstats();
2588 	if (!epktrcvd && !epktsent && !cxseen && !czseen) {
2589 	    xxscreen(SCR_ST,ST_OK,0L,"");
2590 #ifdef TLOG
2591 	    if (tralog && !tlogfmt)
2592 	      doxlog(what,rfspec,fsize,binary,0,"");
2593 #endif /* TLOG */
2594 	}
2595     }
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. */
2600 }
2601 
2602 #ifdef SUNOS4S5
tolower(c)2603 tolower(c) char c; { return((c)-'A'+'a'); }
toupper(c)2604 toupper(c) char c; { return((c)-'a'+'A'); }
2605 #endif /* SUNOS4S5 */
2606 #endif /* NOXFER */
2607