Standards-Version: 3.9.6 (no changes)
[ckermit.git] / ckcfn3.c
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 *
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
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
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
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
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
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
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
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
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
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
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
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
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
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
2603 tolower(c) char c; { return((c)-'A'+'a'); }
2604 toupper(c) char c; { return((c)-'a'+'A'); }
2605 #endif /* SUNOS4S5 */
2606 #endif /* NOXFER */