apply 010_makefile-destdir-support
[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, 2004,
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 extern long sendstart, calibrate, 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 fsize, filcnt, ffc, tfc;
269
270 #ifndef NOCSETS
271 _PROTOTYP (VOID setxlate, (void));
272 extern int tcharset, fcharset;
273 extern int ntcsets, xlatype, xfrxla;
274 extern struct csinfo tcsinfo[], fcsinfo[];
275 #endif /* NOCSETS */
276
277 /* Variables global to Kermit that are defined in this module */
278
279 #ifdef CKXXCHAR                         /* DOUBLE / IGNORE char table */
280 int dblflag = 0;
281 int ignflag = 0;
282 short dblt[256] = {
283     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,
284     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,
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 };
292 #endif /* CKXXCHAR */
293
294 int winlo;                              /* packet number at low window edge  */
295
296 int sbufnum;                            /* number of free buffers */
297 int dum001 = 1234;                      /* protection... */
298 int sbufuse[MAXWS];                     /* buffer in-use flag */
299 int dum003 = 1111;
300 int rbufnum;                            /* number of free buffers */
301 int dum002 = 4321;                      /* more protection */
302 int rbufuse[MAXWS];                     /* buffer in-use flag */
303 int sseqtbl[64];                        /* sequence # to buffer # table */
304 int rseqtbl[64];                        /* sequence # to buffer # table */
305 int sacktbl[64];                        /* sequence # ack table */
306
307 int o_isopen = 0, i_isopen = 0;         /* Input & output files are open */
308
309 #ifdef DYNAMIC
310 struct pktinfo *s_pkt = NULL;           /* array of pktinfo structures */
311 struct pktinfo *r_pkt = NULL;           /* array of pktinfo structures */
312 #else
313 struct pktinfo s_pkt[MAXWS];            /* array of pktinfo structures */
314 struct pktinfo r_pkt[MAXWS];            /* array of pktinfo structures */
315 #endif /* DYNAMIC */
316
317 #ifdef DEBUG
318 char xbuf[200];                         /* For debug logging */
319 #endif /* DEBUG */
320
321 #ifdef DYNAMIC
322 CHAR *bigsbuf = NULL, *bigrbuf = NULL;
323 #else
324 char bigsbt[8];                         /* Protection (shouldn't need this). */
325                                         /* BUT DON'T REMOVE IT! */
326 CHAR bigsbuf[SBSIZ + 5];                /* Send-packet buffer area */
327 char bigrbt[8];                         /* Safety padding */
328 CHAR bigrbuf[RBSIZ + 5];                /* Receive-packet area */
329 #endif
330 int bigsbsiz = SBSIZ;                   /* Sizes of big send & rcv buffers. */
331 int bigrbsiz = RBSIZ;
332
333 #ifdef VMS
334 int zchkpath(char *s);
335 #endif /* VMS */
336
337 /* FUNCTIONS */
338
339 VOID
340 dofast() {
341     long maxbufsiz = RBSIZ;             /* Configuration parameters */
342     int maxpktsiz = MAXSP;
343     extern int spsizf,                  /* For bug in IRIX Telnet server */
344       rpsiz, urpsiz, spsizr, spmax, wslotr;
345     extern struct ck_p ptab[];
346
347     if (maxpktsiz < 40)                 /* Long packet length */
348       maxpktsiz = 40;
349     else if (maxpktsiz > 4000)
350       maxpktsiz = 4000;
351     wslotr = maxbufsiz / maxpktsiz;
352     if (wslotr > MAXWS)                 /* Window slots */
353       wslotr = MAXWS;
354     if (wslotr > 30)
355       wslotr = 30;
356     else if (wslotr < 1)
357       wslotr = 1;
358     urpsiz = adjpkl(maxpktsiz,wslotr,maxbufsiz);
359     ptab[PROTO_K].rpktlen = urpsiz;
360     rpsiz = (urpsiz > 94) ? 94 : urpsiz; /* Max non-long packet length */
361     debug(F111,"dofast","uprsiz",urpsiz);
362 #ifdef IRIX
363 #ifndef IRIX65
364     /* IRIX Telnet server chops off writes longer than 4K */
365     spsiz = spmax = spsizr = urpsiz;
366     debug(F101,"doarg Q IRIX spsiz","",spsiz);
367     spsizf = 1;
368 #endif /* IRIX65 */
369 #endif /* IRIX */
370 #ifdef CK_SPEED
371     setprefix(PX_CAU);                  /* Cautious unprefixing */
372 #endif /* CK_SPEED */
373 }
374
375
376 /* For sanity, use "i" for buffer slots, "n" for packet numbers. */
377
378 /* I N I B U F S */
379
380 /*
381   Allocates the big send and receive buffers.
382   Call with size for big send buffer (s) and receive buffer (r).
383   These sizes can be different.
384   Attempts to allocate buffers of the requested size, but if it can't,
385   it will allocate smaller ones.
386   Sets global variables bigsbsiz and bigrbsiz to the actual sizes,
387   and bigsbuf and bigrbuf pointing to the actual buffers.
388   Designed to be called more than once.
389   Returns 0 on success, -1 on failure.
390 */
391
392 CHAR *bigbufp = NULL;
393
394 int
395 inibufs(s,r) int s, r; {
396 #ifdef DYNAMIC
397     unsigned
398       int size;
399 #ifdef OS2
400     unsigned            /* Don't you wish everybody had unsigned long... */
401 #endif /* OS2 */
402       long z;
403     int x;
404
405     debug(F101,"inibufs s","",s);
406     debug(F101,"inibufs r","",r);
407
408     if (s < 80 || r < 80) return(-1);   /* Validate arguments. */
409
410     if (!s_pkt) {                       /* Allocate packet info structures */
411         if (!(s_pkt = (struct pktinfo *) malloc(sizeof(struct pktinfo)*MAXWS)))
412           fatal("ini_pkts: no memory for s_pkt");
413     }
414     for (x = 0; x < MAXWS; x++)
415       s_pkt[x].pk_adr = NULL;           /* Initialize addresses */
416
417     if (!r_pkt) {
418         if (!(r_pkt = (struct pktinfo *) malloc(sizeof(struct pktinfo)*MAXWS)))
419           fatal("ini_pkts: no memory for s_pkt");
420     }
421     for (x = 0; x < MAXWS; x++)
422       r_pkt[x].pk_adr = NULL;           /* Initialize addresses */
423
424     if (!srvcmd) {                      /* Allocate srvcmd buffer */
425         srvcmd = (CHAR *) malloc(r + 100);
426         if (!srvcmd) return(-1);
427         srvcmdlen = r + 99;
428         *srvcmd = NUL;
429     }
430     if (bigbufp) {                      /* Free previous buffers, if any. */
431         free(bigbufp);
432         bigbufp = NULL;
433     }
434     size = s + r + 40;                  /* Combined requested size + padding */
435     z  = (unsigned) s + (unsigned) r + 40;
436     debug(F101,"inibufs size 1","",size);
437     debug(F101,"inibufs size z","",z);
438     if ((long) size != z) {
439         debug(F100,"inibufs overflow","",0);
440         size = 65535;
441     }
442
443     /* Try to get the space.  If malloc fails, try to get a little less. */
444     /* (Obviously, this algorithm can be refined.) */
445
446     while (!(bigbufp = (CHAR *) malloc(size))) {
447         debug(F101,"inibufs bigbuf malloc failed","",size);
448         size = (size * 2) / 3;          /* Failed, cut size by 1/3. */
449         if (size < 200)                 /* Try again until too small. */
450           return(-1);
451     }
452     debug(F101,"inibufs size 2","",size); /* OK, we got some space. */
453
454 /*
455   Now divide the allocated space between the send and receive buffers in the
456   requested proportion.  The natural formula would be (s / (s + r)) * size
457   (for the send buffer), but that doesn't work with integer arithmetic and we
458   can't use floating point because some machines don't have it.  This can be
459   rearranged as (s * size) / (s + r).  But (s * size) can be VERY large, too
460   large for 32 bits.  So let's do it this way.  This arithmetic works for
461   buffer sizes up to about 5,000,000.
462 */
463 #define FACTOR 20L
464     z = ( (long) s * FACTOR ) / ( (long) s + (long) r );
465     x = ( z * ( (long) size / FACTOR ) );
466     if (x < 0) return(-1);              /* Catch overflow */
467
468     bigsbsiz = x - 5;                   /* Size of send buffer */
469     bigsbuf = bigbufp;                  /* Address of send buffer */
470     debug(F101,"inibufs bigsbsiz","",bigsbsiz);
471
472     bigrbsiz = size - x - 5;            /* Size of receive buffer */
473     bigrbuf = bigbufp + x;              /* Addresss of receive buffer */
474     debug(F101,"inibufs bigrbsiz","",bigrbsiz);
475
476     return(0);                          /* Success */
477 #else                                   /* No dynamic allocation */
478     bigsbsiz = SBSIZ;                   /* Just use the symbols */
479     bigrbsiz = RBSIZ;                   /* ... */
480     return(0);                          /* Success. */
481 #endif /* DYNAMIC */
482 }
483
484
485 /* M A K E B U F  --  Makes and clears a new buffers.  */
486
487 /* Call with: */
488 /*  slots:  number of buffer slots to make, 1 to 32 */
489 /*  bufsiz: size of the big buffer */
490 /*  buf:    address of the big buffer */
491 /*  xx:     pointer to array of pktinfo structures for these buffers */
492
493 /* Subdivides the big buffer into "slots" buffers. */
494
495 /* Returns: */
496 /*  -1 if too many or too few slots requested,     */
497 /*  -2 if slots would be too small.      */
498 /*   n (positive) on success = size of one buffer. */
499 /*   with pktinfo structure initialized for this set of buffers. */
500
501 int
502 makebuf(slots,bufsiz,buf,xx)
503 /* makebuf */ int slots, bufsiz; CHAR buf[]; struct pktinfo *xx; {
504
505     CHAR *a;
506     int i, size;
507
508     debug(F101,"makebuf","",slots);
509     debug(F101,"makebuf bufsiz","",bufsiz);
510     debug(F101,"makebuf MAXWS","",MAXWS);
511
512     if (slots > MAXWS || slots < 1) return(-1);
513     if (bufsiz < slots * 10 ) return(-2);
514
515     size = bufsiz / slots;              /* Divide up the big buffer. */
516     a = buf;                            /* Address of first piece. */
517
518     for (i = 0; i < slots; i++) {
519         struct pktinfo *x = &xx[i];
520         x->bf_adr = a;                  /* Address of this buffer */
521         x->bf_len = size;               /* Length of this buffer */
522         x->pk_len = 0;                  /* Length of data field */
523         x->pk_typ = ' ';                /* packet type */
524         x->pk_seq = -1;                 /* packet sequence number */
525         x->pk_rtr = 0;                  /* retransmissions */
526         *a = '\0';                      /* Clear the buffer */
527         a += size;                      /* Position to next buffer slot */
528     }
529     return(size);
530 }
531
532 /*  M A K S B U F  --  Makes the send-packet buffer  */
533
534 int
535 mksbuf(slots) int slots; {
536     int i, x;
537     sbufnum = 0;
538     if ((x = makebuf(slots,bigsbsiz,bigsbuf,s_pkt)) < 0) {
539         debug(F101,"mksbuf makebuf return","",x);
540         return(x);
541     }
542     debug(F101,"mksbuf makebuf return","",x);
543     for (i = 0; i < 64; i++) {          /* Initialize sequence-number- */
544         sseqtbl[i] = -1;                /* to-buffer-number table. */
545         sacktbl[i] = 0;
546     }
547     for (i = 0; i < MAXWS; i++)
548       sbufuse[i] = 0;                   /* Mark each buffer as free */
549     sbufnum = slots;
550     wcur = 0;
551     return(x);
552 }
553
554 /*  M A K R B U F  --  Makes the receive-packet buffer  */
555
556 int
557 mkrbuf(slots) int slots; {
558     int i, x;
559     rbufnum = 0;
560     if ((x = makebuf(slots,bigrbsiz,bigrbuf,r_pkt)) < 0) {
561         debug(F101,"mkrbuf makebuf return","",x);
562         return(x);
563     }
564     debug(F101,"mkrbuf makebuf return","",x);
565     for (i = 0; i < 64; i++) {          /* Initialize sequence-number- */
566         rseqtbl[i] = -1;                /* to-buffer-number table. */
567     }
568     for (i = 0; i < MAXWS; i++)
569       rbufuse[i] = 0;                   /* Mark each buffer as free */
570     rbufnum = slots;
571     wcur = 0;
572     return(x);
573 }
574
575 /*  W I N D O W  --  Resize the window to n  */
576
577 int
578 window(n) int n; {
579     debug(F101,"window","",n);
580     if (n < 1 || n > MAXWS) return(-1);
581     if (mksbuf(n) < 0) return(-1);
582     if (mkrbuf(n) < 0) return(-1);
583     wslots = n;
584 #ifdef DEBUG
585     if (deblog) dumpsbuf();
586     if (deblog) dumprbuf();
587 #endif /* DEBUG */
588     return(0);
589 }
590
591 /*  G E T S B U F  --  Allocate a send-buffer.  */
592
593 /*  Call with packet sequence number to allocate buffer for. */
594 /*  Returns: */
595 /*   -4 if argument is invalid (negative, or greater than 63) */
596 /*   -3 if buffers were thought to be available but really weren't (bug!) */
597 /*   -2 if the number of free buffers is negative (bug!) */
598 /*   -1 if no free buffers. */
599 /*   0 or positive, packet sequence number, with buffer allocated for it. */
600
601 int
602 getsbuf(n) int n; {                     /* Allocate a send-buffer */
603     int i;
604     CHAR * p = NULL;
605     if (n < 0 || n > 63) {
606         debug(F101,"getsbuf bad arg","",n);
607         return(-4);     /* Bad argument */
608     }
609     debug(F101,"getsbuf packet","",n);
610     /* debug(F101,"getsbuf, sbufnum","",sbufnum); */
611     if (sbufnum == 0) return(-1);       /* No free buffers. */
612     if (sbufnum < 0) return(-2);        /* Shouldn't happen. */
613     for (i = 0; i < wslots; i++)        /* Find the first one not in use. */
614       if (sbufuse[i] == 0) {            /* Got one? */
615           sbufuse[i] = 1;               /* Mark it as in use. */
616           sbufnum--;                    /* One less free buffer. */
617           *s_pkt[i].bf_adr = '\0';      /* Zero the buffer data field */
618           s_pkt[i].pk_seq = n;          /* Put in the sequence number */
619           sseqtbl[n] = i;               /* Back pointer from sequence number */
620           sacktbl[n] = 0;               /* ACK flag */
621           s_pkt[i].pk_len = 0;          /* Data field length now zero. */
622           s_pkt[i].pk_typ = ' ';        /* Blank the packet type too. */
623           s_pkt[i].pk_rtr = 0;          /* Zero the retransmission count */
624           p = s_pkt[i].bf_adr + 7;      /* Set global "data" address. */
625           debug(F101,"getsbuf p","",0);
626           data = p;
627           if (!data) {
628               debug(F100,"getsbuf data == NULL","",0);
629               return(-3);
630           }
631           if ((what & (W_SEND|W_REMO)) && (++wcur > wmax))
632             wmax = wcur;                /* For statistics. */
633           /* debug(F101,"getsbuf wcur","",wcur); */
634           return(n);                    /* Return its index. */
635       }
636     sbufnum = 0;                        /* Didn't find one. */
637     return(-3);                         /* Shouldn't happen! */
638 }
639
640 int
641 getrbuf() {                             /* Allocate a receive buffer */
642     int i;
643 #ifdef COMMENT
644     /* This code is pretty stable by now... */
645     /* Looks like we might need this after all */
646     debug(F101,"getrbuf rbufnum","",rbufnum);
647     debug(F101,"getrbuf wslots","",wslots);
648     debug(F101,"getrbuf dum002","",dum002);
649     debug(F101,"getrbuf dum003","",dum003);
650 #endif /* COMMENT */
651     if (rbufnum == 0) return(-1);       /* No free buffers. */
652     if (rbufnum < 0) return(-2);        /* Shouldn't happen. */
653     for (i = 0; i < wslots; i++)        /* Find the first one not in use. */
654       if (rbufuse[i] == 0) {            /* Got one? */
655           rbufuse[i] = 1;               /* Mark it as in use. */
656           *r_pkt[i].bf_adr = '\0';      /* Zero the buffer data field */
657           rbufnum--;                    /* One less free buffer. */
658           debug(F101,"getrbuf new rbufnum","",rbufnum);
659           if ((what & W_RECV) && (++wcur > wmax))
660             wmax = wcur;                /* For statistics. */
661           /* debug(F101,"getrbuf wcur","",wcur); */
662           return(i);                    /* Return its index. */
663       }
664     /* debug(F101,"getrbuf foulup","",i); */
665     rbufnum = 0;                        /* Didn't find one. */
666     return(-3);                         /* Shouldn't happen! */
667 }
668
669 /*  F R E E S B U F  --  Free send-buffer for given packet sequence number */
670
671 /*  Returns:  */
672 /*   1 upon success  */
673 /*  -1 if specified buffer does not exist */
674
675 int
676 freesbuf(n) int n; {                    /* Release send-buffer for packet n. */
677     int i;
678
679     debug(F101,"freesbuf","",n);
680     if (n < 0 || n > 63)                /* No such packet. */
681       return(-1);
682     i = sseqtbl[n];                     /* Get the window slot number. */
683     if (i > -1 && i <= wslots) {
684         sseqtbl[n] = -1;                /* If valid, remove from seqtbl */
685         sbufnum++;                      /* and count one more free buffer */
686         sbufuse[i] = 0;                 /* and mark it as free, */
687         if (what & (W_SEND|W_REMO))     /* decrement active slots */
688           wcur--;                       /* for statistics and display. */
689     } else {
690         debug(F101," sseqtbl[n]","",sseqtbl[n]);
691         return(-1);
692     }
693
694 /* The following is done only so dumped buffers will look right. */
695
696     if (1) {
697         *s_pkt[i].bf_adr = '\0';        /* Zero the buffer data field */
698         s_pkt[i].pk_seq = -1;           /* Invalidate the sequence number */
699         s_pkt[i].pk_len = 0;            /* Data field length now zero. */
700         s_pkt[i].pk_typ = ' ';          /* Blank the packet type too. */
701         s_pkt[i].pk_rtr = 0;            /* And the retries field. */
702     }
703     return(1);
704 }
705
706 int
707 freerbuf(i) int i; {                    /* Release receive-buffer slot "i". */
708     int n;
709
710 /* NOTE !! Currently, this function frees the indicated buffer, but */
711 /* does NOT erase the data.  The program counts on this.  Will find a */
712 /* better way later.... */
713
714     /* debug(F101,"freerbuf, slot","",i); */
715     if (i < 0 || i >= wslots) {         /* No such slot. */
716         debug(F101,"freerbuf no such slot","",i);
717         return(-1);
718     }
719     n = r_pkt[i].pk_seq;                /* Get the packet sequence number */
720     debug(F101,"freerbuf packet","",n);
721     if (n > -1 && n < 64)               /* If valid, remove from seqtbl */
722       rseqtbl[n] = -1;
723     if (rbufuse[i] != 0) {              /* If really allocated, */
724         rbufuse[i] = 0;                 /* mark it as free, */
725         rbufnum++;                      /* and count one more free buffer. */
726         if (what & W_RECV)              /* Keep track of current slots */
727           wcur--;                       /*  for statistics and display */
728         debug(F101,"freerbuf rbufnum","",rbufnum);
729     }
730
731 /* The following is done only so dumped buffers will look right. */
732
733     if (1) {
734      /* *r_pkt[i].bf_adr = '\0'; */     /* Zero the buffer data field */
735         r_pkt[i].pk_seq = -1;           /* And from packet list */
736         r_pkt[i].pk_len = 0;            /* Data field length now zero. */
737         r_pkt[i].pk_typ = ' ';          /* Blank the packet type too. */
738         r_pkt[i].pk_rtr = 0;            /* And the retries field. */
739     }
740     return(1);
741 }
742
743 /* This is like freerbuf, except it's called with a packet sequence number */
744 /* rather than a packet buffer index. */
745
746 VOID
747 freerpkt(seq) int seq; {
748     int k;
749     debug(F101,"freerpkt seq","",seq);
750     k = rseqtbl[seq];
751     /* debug(F101,"freerpkt k","",k); */
752     if (k > -1) {
753         k = freerbuf(k);
754         /* debug(F101,"freerpkt freerbuf","",k); */
755     }
756 }
757
758
759 /*  C H K W I N  --  Check if packet n is in window. */
760
761 /*  Returns: */
762 /*    0 if it is in the current window,  */
763 /*   +1 if it would have been in previous window (e.g. if ack was lost), */
764 /*   -1 if it is outside any window (protocol error),   */
765 /*   -2 if either of the argument packet numbers is out of range.  */
766
767 /* Call with packet number to check (n), lowest packet number in window */
768 /* (bottom), and number of slots in window (slots).  */
769
770 int
771 chkwin(n,bottom,slots) int n, bottom, slots; {
772     int top, prev;
773
774     debug(F101,"chkwin packet","",n);
775     debug(F101,"chkwin winlo","",bottom);
776     debug(F101,"chkwin slots","",slots);
777
778 /* First do the easy and common cases, where the windows are not split. */
779
780     if (n < 0 || n > 63 || bottom < 0 || bottom > 63)
781       return(-2);
782
783     if (n == bottom) return(0);         /* In a perfect world... */
784
785     top = bottom + slots;               /* Calculate window top. */
786     if (top < 64 && n < top && n >= bottom)
787       return(0);                        /* In current window. */
788
789     prev = bottom - slots;              /* Bottom of previous window. */
790     if (prev > -1 && n < bottom && n > prev)
791       return(1);                        /* In previous. */
792
793 /* Now consider the case where the current window is split. */
794
795     if (top > 63) {                     /* Wraparound... */
796         top -= 64;                      /* Get modulo-64 sequence number */
797         if (n < top || n >= bottom) {
798             return(0);                  /* In current window. */
799         } else {                        /* Not in current window. */
800             if (n < bottom && n >= prev) /* Previous window can't be split. */
801               return(1);                /* In previous window. */
802             else
803               return(-1);               /* Not in previous window. */
804         }
805     }
806
807 /* Now the case where current window not split, but previous window is. */
808
809     if (prev < 0) {                     /* Is previous window split? */
810         prev += 64;                     /* Yes. */
811         if (n < bottom || n >= prev)
812           return(1);                    /* In previous window. */
813     } else {                            /* Previous window not split. */
814         if (n < bottom && n >= prev)
815           return(1);                    /* In previous window. */
816     }
817
818 /* It's not in the current window, and not in the previous window... */
819
820     return(-1);                         /* So it's not in any window. */
821 }
822
823 int
824 dumpsbuf() {                            /* Dump send-buffers */
825 #ifdef DEBUG
826     int j, x, z;                        /* to debug log. */
827
828     if (! deblog) return(0);
829     x = zsoutl(ZDFILE,"SEND BUFFERS:");
830     if (x < 0) {
831         deblog = 0;
832         return(0);
833     }
834     x=zsoutl(ZDFILE,"buffer inuse address length data type seq flag retries");
835     if (x < 0) {
836         deblog = 0;
837         return(0);
838     }
839     for (j = 0; j < wslots; j++) {
840         if (!sbufuse[j])
841           continue;
842         z = ((unsigned long)(s_pkt[j].bf_adr)) & 0xffff;
843
844         sprintf(xbuf,                   /* safe (200) */
845                 "%4d%6d%10d%5d%6d%4c%5d%6d\n",
846                 j,
847                 sbufuse[j],
848                 /* Avoid warnings when addresses are bigger than ints */
849                 z,
850                 s_pkt[j].bf_len,
851                 s_pkt[j].pk_len,
852                 s_pkt[j].pk_typ,
853                 s_pkt[j].pk_seq,
854                 s_pkt[j].pk_rtr
855                 );
856         if (zsout(ZDFILE,xbuf) < 0)  {
857             deblog = 0;
858             return(0);
859         }
860         if (s_pkt[j].pk_adr) {
861             x = (int)strlen((char *) s_pkt[j].pk_adr);
862             if (x)
863               sprintf(xbuf,             /* safe (checked) */
864                       "[%.72s%s]\n",s_pkt[j].pk_adr, x > 72 ? "..." : "");
865             else
866               sprintf(xbuf,"[(empty string)]\n"); /* safe (200) */
867         } else {
868             sprintf(xbuf,"[(null pointer)]\n"); /* safe (200) */
869         }
870         if (zsout(ZDFILE,xbuf) < 0) {
871             deblog = 0;
872             return(0);
873         }
874     }
875     sprintf(xbuf,"free: %d, winlo: %d\n", sbufnum, winlo); /* safe (200) */
876     if (zsout(ZDFILE,xbuf) < 0) {
877         deblog = 0;
878         return(0);
879     }
880 #endif /* DEBUG */
881     return(0);
882 }
883 int
884 dumprbuf() {                            /* Dump receive-buffers */
885 #ifdef DEBUG
886     int j, x, z;
887     if (! deblog) return(0);
888     if (zsoutl(ZDFILE,"RECEIVE BUFFERS:") < 0) {
889         deblog = 0;
890         return(0);
891     }
892     x=zsoutl(ZDFILE,"buffer inuse address length data type seq flag retries");
893     if (x < 0) {
894         deblog = 0;
895         return(0);
896     }
897     for ( j = 0; j < wslots; j++ ) {
898         if (!rbufuse[j])
899           continue;
900         z = ((unsigned long)(r_pkt[j].bf_adr)) & 0xffff;
901         sprintf(xbuf,                   /* 200, safe */
902                 "%4d%6d%10d%5d%6d%4c%5d%6d\n",
903                 j,
904                 rbufuse[j],
905                 /* Avoid warnings when addresses are bigger than ints */
906                 z,
907                 r_pkt[j].bf_len,
908                 r_pkt[j].pk_len,
909                 r_pkt[j].pk_typ,
910                 r_pkt[j].pk_seq,
911                 r_pkt[j].pk_rtr
912                 );
913         if (zsout(ZDFILE,xbuf) < 0) {
914             deblog = 0;
915             return(0);
916         }
917         x = (int)strlen((char *)r_pkt[j].bf_adr);
918         sprintf(xbuf,                   /* safe (checked) */
919                 "[%.72s%s]\n",r_pkt[j].bf_adr, x > 72 ? "..." : "");
920         if (zsout(ZDFILE,xbuf) < 0)  {
921             deblog = 0;
922             return(0);
923         }
924     }
925     sprintf(xbuf,"free: %d, winlo: %d\n", rbufnum, winlo); /* safe (200) */
926     if (zsout(ZDFILE,xbuf) < 0)  {
927         deblog = 0;
928         return(0);
929     }
930 #endif /* DEBUG */
931     return(0);
932 }
933
934 /*  S A T T R  --  Send an Attribute Packet  */
935
936 /*
937   Sends attribute packet(s) for the current file.  If the info will not
938   fit into one packet, it can be called repeatedly until all the fields
939   that will fit are sent.
940
941   Call with:
942     xp == 0 if we're sending a real file (F packet), or:
943     xp != 0 for screen data (X packet).
944   And:
945     flag == 1 for first A packet
946     flag == 0 for subsequent A packets.
947   Returns:
948     1 or greater if an A packet was sent, or:
949     0 if an S-packet was not sent because there was no data to send or
950       there was no data left that was short enough to send, or:
951    -1 on any kind of error.
952 */
953
954 /* (don't) #define TSOFORMAT */
955 /* which was only for making C-Kermit send TSO-Kermit-like A packets */
956 /* to try to track down a problem somebody reported... */
957
958 int
959 sattr(xp, flag) int xp, flag; {         /* Send Attributes */
960
961     static int max;                     /* Maximum length for Attributes */
962     static short done[95];              /* Field-complete array */
963     static struct zattr x;              /* File attribute struct */
964     static char xdate[24];
965
966     extern char * cksysid;
967
968     /* Some extra flags are used because the "done" array is sparse */
969
970     int i, j, rc, aln, left = 0, numset = 0, xbin = 0; /* Workers */
971     int notafile = 0;
972     char *tp, c;
973
974     notafile = sndarray || pipesend ||
975 #ifdef PIPESEND
976       sndfilter ||
977 #endif /* PIPESEND */
978         calibrate;
979
980     debug(F101,"sattr flag","",flag);
981     if (!flag)                          /* No more attributes to send */
982       if (done[xunchar('@')])
983         return(0);
984
985     /* Initialize Attribute mechanism */
986
987     if (flag) {                         /* First time here for this file? */
988         initattr(&x);                   /* Blank out all the fields. */
989         for (j = 0; j < 95; j++)        /* Init array of completed fields */
990           done[j] = 0;
991         max = maxdata();                /* Get maximum data field length */
992         if (notafile || xp == 1) {      /* Is it not a real file? */
993             extern char * zzndate();
994             char * p;
995             int i;
996 #ifdef CALIBRATE
997             if (calibrate) {            /* Calibration run... */
998                 x.lengthk = calibrate / 1024L; /* We know the length */
999                 x.length = calibrate;
1000             }
1001 #endif /* CALIBRATE */
1002             x.systemid.val = cksysid;   /* System ID */
1003             x.systemid.len = (int)strlen(cksysid);
1004             ckstrncpy(xdate,zzndate(),24);
1005             xdate[8] = SP;
1006             ztime(&p);
1007             for (i = 11; i < 19; i++)   /* copy hh:mm:ss */
1008               xdate[i - 2] = p[i];      /* to xdate */
1009             xdate[17] = NUL;            /* terminate */
1010             x.date.val = xdate;
1011             x.date.len = 17;
1012             debug(F111,"sattr notafile date",x.date.val,x.date.len);
1013         } else {                        /* Real file */
1014             rc = zsattr(&x);            /* Get attributes for this file  */
1015             debug(F101,"sattr zsattr","",rc);
1016             if (rc < 0)                 /* Can't get 'em so don't send 'em */
1017               return(0);
1018             debug(F101,"sattr init max","",max);
1019         }
1020     }
1021     if (nxtpkt() < 0)                   /* Got 'em, get next packet number */
1022       return(-1);                       /* Bad news if we can't */
1023
1024     i = 0;                              /* Init data field character number */
1025
1026     /* Do each attribute using first-fit method, marking as we go */
1027     /* This is rather long and repititious - could be done more cleverly */
1028
1029     if (atsido && !done[xunchar(c = '.')]) { /* System type */
1030         if (max - i >= x.systemid.len + 2) { /* Enough space ? */
1031             data[i++] = c;                   /* Yes, add parameter */
1032             data[i++] = tochar(x.systemid.len);  /* Add length */
1033             for (j = 0; j < x.systemid.len; j++) /* Add data */
1034               data[i++] = x.systemid.val[j];
1035             numset++;                   /* Count that we did at least one */
1036             done[xunchar(c)] = 1;       /* Mark this attribute as done */
1037         } else                          /* No */
1038           left++;                       /* so mark this one left to do */
1039     }
1040 #ifdef STRATUS
1041     if (atcreo && !done[xunchar(c = '$')]) { /* Creator */
1042         if (max - i >= x.creator.len + 2) { /* Enough space ? */
1043             data[i++] = c;
1044             data[i++] = tochar(x.creator.len);
1045             for (j = 0; j < x.creator.len; j++)
1046               data[i++] = x.creator.val[j];
1047             numset++;
1048             done[xunchar(c)] = 1;
1049         } else
1050           left++;
1051     }
1052     if (atacto && !done[xunchar(c = '%')]) { /* File account */
1053         if (max - i >= x.account.len + 2) {
1054             data[i++] = c;
1055             data[i++] = tochar(x.account.len);
1056             for (j = 0; j < x.account.len; j++)
1057               data[i++] = x.account.val[j];
1058             numset++;
1059             done[xunchar(c)] = 1;
1060         } else
1061           left++;
1062     }
1063     if (atfrmo && !done[xunchar(c = '/')]) { /* Packet data format */
1064         if (max - i >= x.recfm.len + 2) {
1065             data[i++] = c;
1066             data[i++] = tochar(x.recfm.len); /*  Copy from attr structure */
1067             for (j = 0; j < x.recfm.len; j++)
1068               data[i++] = x.recfm.val[j];
1069             numset++;
1070             done[xunchar(c)] = 1;
1071         } else
1072           left++;
1073     }
1074 #endif /* STRATUS */
1075
1076     xbin =                              /* Is the transfer in binary mode? */
1077 #ifdef VMS
1078       binary == XYFT_I || binary == XYFT_L || /* IMAGE or LABELED */
1079         !strncmp(x.recfm.val,"F",1)     /* or RECFM=Fxxxxxx */
1080 #else
1081       binary                            /* User said SET FILE TYPE BINARY  */
1082 #endif /* VMS */
1083         ;
1084
1085     if (attypo && !done[xunchar(c = '"')]) { /* File type */
1086         if (max - i >= 5) {             /* Max length for this field */
1087             data[i++] = c;
1088             if (xbin) {                 /* Binary */
1089                 data[i++] = tochar(2);  /*  Two characters */
1090                 data[i++] = 'B';        /*  B for Binary */
1091                 data[i++] = '8';        /*  8-bit bytes (note assumption...) */
1092 #ifdef CK_LABELED
1093                 if (binary != XYFT_L
1094 #ifdef VMS
1095                     && binary != XYFT_I
1096 #endif /* VMS */
1097                     )
1098                   binary = XYFT_B;
1099 #endif /* CK_LABELED */
1100             } else {                    /* Text */
1101 #ifdef TSOFORMAT
1102                 data[i++] = tochar(1);  /*  One character */
1103                 data[i++] = 'A';        /*  A = (extended) ASCII with CRLFs */
1104 #else
1105                 data[i++] = tochar(3);  /*  Three characters */
1106                 data[i++] = 'A';        /*  A = (extended) ASCII with CRLFs */
1107                 data[i++] = 'M';        /*  M for carriage return */
1108                 data[i++] = 'J';        /*  J for linefeed */
1109 #endif /* TSOFORMAT */
1110
1111 #ifdef VMS
1112                 binary = XYFT_T;        /* We automatically detected text */
1113 #endif /* VMS */
1114             }
1115             numset++;
1116             done[xunchar(c)] = 1;
1117         } else
1118           left++;
1119     }
1120
1121 #ifdef TSOFORMAT
1122     if (attypo && !xbin && !done[xunchar(c = '/')]) { /* Record format */
1123         if (max - i >= 5) {
1124             data[i++] = c;
1125             data[i++] = tochar(3);      /*  Three characters */
1126             data[i++] = 'A';            /*  A = variable with CRLFs */
1127             data[i++] = 'M';            /*  M for carriage return */
1128             data[i++] = 'J';            /*  J for linefeed */
1129         }
1130     }
1131 #endif /* TSOFORMAT */
1132
1133     if (attypo && !xbin && !done[xunchar(c = '*')]) { /* Text encoding */
1134 #ifdef NOCSETS
1135         if (max - i >= 3) {
1136             data[i++] = c;
1137             data[i++] = tochar(1);      /* Length of value is 1 */
1138             data[i++] = 'A';            /* A for ASCII */
1139             numset++;
1140             done[xunchar(c)] = 1;
1141         } else
1142           left++;
1143 #else
1144         if (tcharset == TC_TRANSP || !xfrxla) { /* Transfer character set */
1145             if (max - i >= 3) {
1146                 data[i++] = c;          /* Encoding */
1147                 data[i++] = tochar(1);  /* Length of value is 1 */
1148                 data[i++] = 'A';        /* A for ASCII (i.e. text) */
1149                 numset++;
1150                 done[xunchar(c)] = 1;
1151             } else
1152               left++;
1153         } else {
1154             tp = tcsinfo[tcharset].designator;
1155             if (!tp) tp = "";
1156             aln = strlen(tp);
1157             if (aln > 0) {
1158                 if (max - i >= aln + 2) {
1159                     data[i++] = c;      /* Encoding */
1160                     data[i++] = tochar(aln+1); /* Length of designator. */
1161                     data[i++] = 'C'; /* Text in specified charset. */
1162                     for (j = 0; j < aln; j++) /* Copy designator */
1163                       data[i++] = *tp++; /*  Example: *&I6/100 */
1164                     numset++;
1165                     done[xunchar(c)] = 1;
1166                 } else
1167                   left++;
1168             } else
1169               done[xunchar(c)] = 1;
1170         }
1171 #endif /* NOCSETS */
1172     }
1173     if (atdato && !done[xunchar(c = '#')] && /* Creation date, if any */
1174         (aln = x.date.len) > 0) {
1175         if (max - i >= aln + 2) {
1176             data[i++] = c;
1177             data[i++] = tochar(aln);
1178             for (j = 0; j < aln; j++)
1179               data[i++] = x.date.val[j];
1180             numset++;
1181             done[xunchar(c)] = 1;
1182         } else
1183           left++;
1184     }
1185     /* File length in K */
1186     if (atleno && !done[xunchar(c = '!')] && x.lengthk > -1L) {
1187         sprintf((char *) &data[i+2],"%ld",x.lengthk); /* safe */
1188         aln = (int)strlen((char *)(data+i+2));
1189         if (max - i >= aln + 2) {
1190             data[i] = c;
1191             data[i+1] = tochar(aln);
1192             i += aln + 2;
1193             numset++;
1194             done[xunchar(c)] = 1;
1195         } else {
1196             data[i] = NUL;
1197             left++;
1198         }
1199     }
1200     /* File length in bytes */
1201     if (atleno && !done[xunchar(c = '1')] && x.length > -1L) {
1202         sprintf((char *) &data[i+2],"%ld",x.length); /* safe */
1203         aln = (int)strlen((char *)(data+i+2));
1204         if (max - i >= aln + 2) {
1205             data[i] = c;
1206             data[i+1] = tochar(aln);
1207             i += aln + 2;
1208             numset++;
1209             done[xunchar(c)] = 1;
1210         } else {
1211             data[i] = NUL;
1212             left++;
1213         }
1214     }
1215 #ifdef CK_PERMS
1216     if (atlpro && !done[xunchar(c = ',')] && /* Local protection */
1217         (aln = x.lprotect.len) > 0 && !notafile && xp == 0) {
1218         if (max - i >= aln + 2) {
1219             data[i++] = c;
1220             data[i++] = tochar(aln);
1221             for (j = 0; j < aln; j++)
1222               data[i++] = x.lprotect.val[j];
1223             numset++;
1224             done[xunchar(c)] = 1;
1225         } else
1226           left++;
1227     }
1228     if (atgpro && !done[xunchar(c = '-')] && /* Generic protection */
1229         (aln = x.gprotect.len) > 0 && !notafile && xp == 0) {
1230         if (max - i >= aln + 2) {
1231             data[i++] = c;
1232             data[i++] = tochar(aln);
1233             for (j = 0; j < aln; j++)
1234               data[i++] = x.gprotect.val[j];
1235             numset++;
1236             done[xunchar(c)] = 1;
1237         } else
1238           left++;
1239     }
1240 #endif /* CK_PERMS */
1241     if (atblko && fblksiz && !done[xunchar(c = '(')] &&
1242         !notafile && xp == 0) { /* Blocksize */
1243         sprintf((char *) &data[i+2],"%d",fblksiz); /* safe */
1244         aln = (int)strlen((char *)(data+i+2));
1245         if (max - i >= aln + 2) {
1246             data[i] = c;
1247             data[i+1] = tochar(aln);
1248             i += aln + 2;
1249             numset++;
1250             done[xunchar(c)] = 1;
1251         } else {
1252             data[i] = NUL;
1253             left++;
1254         }
1255     }
1256 #ifndef NOFRILLS
1257     if ((rprintf || rmailf) && atdiso && /* MAIL, or REMOTE PRINT?  */
1258         !done[xunchar(c = '+')]) {
1259         aln = (int) strlen(optbuf) + 1; /* Options, if any */
1260         if (max - i >= aln + 2) {
1261             data[i++] = c;              /* Disposition */
1262             data[i++] = tochar(aln);    /* Options, if any */
1263             if (rprintf)
1264               data[i++] = 'P';          /* P for Print */
1265             else
1266               data[i++] = 'M';          /* M for Mail */
1267             for (j = 0; optbuf[j]; j++) /* Copy any options */
1268               data[i++] = optbuf[j];
1269             numset++;
1270             done[xunchar(c)] = 1;
1271         } else {
1272             data[i] = NUL;
1273             left++;
1274         }
1275     }
1276 #endif /* NOFRILLS */
1277 #ifdef CK_RESEND
1278     if (sendmode == SM_RESEND && !done[xunchar(c = '+')]) {
1279         if (max - i >= 3) {
1280             data[i++] = c;              /* Disposition */
1281             data[i++] = tochar(1);
1282             data[i++] = 'R';            /* is RESEND */
1283             numset++;
1284             done[xunchar(c)] = 1;
1285         } else
1286           left++;
1287     }
1288 #endif /* CK_RESEND */
1289
1290     /* End of Attributes -- to be sent only after sending all others */
1291
1292     debug(F111,"sattr","@",i);
1293     debug(F101,"sattr numset","",numset);
1294     debug(F101,"sattr left","",left);
1295
1296     if ((left == 0 || numset == 0) && !done[xunchar(c = '@')]) {
1297         if (max - i >= 3) {
1298             data[i++] = c;              /* End of Attributes */
1299             data[i++] = SP;             /* Length 0 */
1300             data[i] = NUL;              /* Make sure it's null-terminated */
1301             numset++;
1302             done[xunchar(c)] = 1;
1303         }
1304     }
1305
1306     /* Finished - send the packet off if we have anything in it */
1307
1308     if (numset) {
1309         data[i] = NUL;                  /* Terminate last good field */
1310         debug(F111,"sattr sending",data,left);
1311         aln = (int)strlen((char *)data); /* Get overall length of attributes */
1312         return(spack('A',pktnum,aln,data)); /* Send it */
1313     } else
1314       return(0);
1315 }
1316
1317 static char *refused = "";
1318
1319 static char *reason[] = {
1320     "size", "type", "date", "creator", "account", "area", "password",
1321     "blocksize", "access", "encoding", "disposition", "protection",
1322     "protection", "origin", "format",
1323     "sys-dependent",                    /* 0 */
1324     "size",                             /* 1 */
1325     "2",                                /* 2 */
1326     "3",                                /* 3 */
1327     "4",                                /* 4 */
1328     "5",                                /* 5 */
1329     "6",                                /* 6 */
1330     "7",                                /* 7 */
1331     "8",                                /* 8 */
1332     "9",                                /* 9 */
1333     ":",                                /* : */
1334     ";",                                /* ; */
1335     "<",                                /* < */
1336     "=",                                /* = */
1337     ">",                                /* > */
1338     "name",                             /* ? */
1339     "@"
1340 };
1341 static int nreason = sizeof(reason) / sizeof(char *);
1342 int rejection = -1;
1343
1344 char *
1345 getreason(s) char *s; {                 /* Decode attribute refusal reason */
1346     char c, *p;
1347     if (rejection == 1)                 /* Kludge for SET FIL COLL DISCARD */
1348       return("name");                   /* when other Kermit doesn't... */
1349     p = s;
1350     if (*p++ != 'N') return("");        /* Should start with N */
1351     else if ((c = *p) > SP) {           /* get reason, */
1352         rejection = c;                  /* remember it, */
1353         c -= '!';                       /* get offset */
1354         p = ((unsigned int) ((CHAR) c) <= (unsigned int) nreason) ?
1355           reason[c] :
1356             "unknown";
1357     }
1358     return(p);
1359 }
1360
1361 int
1362 rsattr(s) CHAR *s; {                    /* Read response to attribute packet */
1363     debug(F111,"rsattr",s,*s);
1364     if (*s == 'N') {                    /* If it's 'N' followed by anything, */
1365         refused = getreason((char *)s); /* they are refusing, get reason. */
1366         debug(F110,"rsattr refused",refused,0);
1367         tlog(F110," refused:",refused,0L);
1368         return(-1);
1369     }
1370 #ifdef CK_RESEND
1371     if (sendmode == SM_RESEND && *s == '1') { /* RESEND length */
1372         int n; long z; CHAR *p;
1373         p = s + 1;
1374         n = xunchar(*p++);
1375         debug(F101,"rsattr RESEND n","",n);
1376         z = 0L;
1377         while (n-- > 0)                 /* We assume the format is good. */
1378           z = 10L * z + (long) (*p++ - '0');
1379         debug(F101,"rsattr RESEND z","",z);
1380         if (z > 0L) sendstart = z;
1381         debug(F101,"rsattr RESEND sendstart","",sendstart);
1382         if (sendstart > 0L)
1383           if (zfseek(sendstart) < 0)    /* Input file is already open. */
1384             return(0);
1385 #ifdef CK_CURSES
1386         if (fdispla == XYFD_C)
1387           xxscreen(SCR_FS,0,fsize,"");  /* Refresh file transfer display */
1388 #endif /* CK_CURSES */
1389     }
1390 #endif /* CK_RESEND */
1391     refused = "";
1392     return(0);
1393 }
1394
1395 long rs_len = 0L;                       /* Length of file being resent to */
1396
1397 /*
1398   Get attributes from incoming A packet.  Returns:
1399    0 on success, file is to be accepted
1400   -1 on failure, file is to be refused
1401 */
1402 int
1403 gattr(s, yy) CHAR *s; struct zattr *yy; { /* Read incoming attribute packet */
1404     char c, d;
1405     char *ff;
1406     int aln, i;
1407
1408 #ifndef NOCSETS
1409     extern int r_cset, axcset[];
1410 #endif /* NOCSETS */
1411
1412 #define ABUFL 40                        /* Temporary buffer for conversions */
1413     char abuf[ABUFL+1];
1414 #define RFBUFL 10                       /* Record-format buffer */
1415     static char rfbuf[RFBUFL+1];
1416 #define FTBUFL 10                       /* File type buffer */
1417     static char ftbuf[FTBUFL+1];
1418 #define DTBUFL 40                       /* File creation date */
1419     static char dtbuf[DTBUFL+1];
1420 #define TSBUFL 10                       /* Transfer syntax */
1421     static char tsbuf[TSBUFL+1];
1422 #define IDBUFL 10                       /* System ID */
1423     static char idbuf[IDBUFL+1];
1424 #ifndef DYNAMIC
1425 #define DSBUFL 100                      /* Disposition */
1426     static char dsbuf[DSBUFL+1];
1427 #define SPBUFL 512                      /* System-dependent parameters */
1428     static char spbuf[SPBUFL+1];
1429 #else
1430 #define DSBUFL 100                      /* Disposition */
1431     static char *dsbuf = NULL;
1432 #define SPBUFL 512                      /* System-dependent parameters */
1433     static char *spbuf = NULL;
1434 #endif /* DYNAMIC */
1435 #define RPBUFL 20                       /* Attribute reply */
1436     static char rpbuf[RPBUFL+1];
1437
1438 #ifdef CK_PERMS
1439     static char lprmbuf[CK_PERMLEN+1];
1440     static char gprmbuf[2];
1441 #endif /* CK_PERMS */
1442
1443     char *rp;                           /* Pointer to reply buffer */
1444     int retcode;                        /* Return code */
1445
1446     d = SP;                             /* Initialize disposition */
1447     ff = filnam;                        /* Filename returned by rcvfil */
1448     if (fncact == XYFX_R && ofn1x && ofn1[0]) /* But watch out for FC=RENAME */
1449       ff = ofn1;                        /* because we haven't renamed it yet */
1450
1451 /* Fill in the attributes we have received */
1452
1453     rp = rpbuf;                         /* Initialize reply buffer */
1454     *rp++ = 'N';                        /* for negative reply. */
1455     *rp = NUL;
1456     retcode = 0;                        /* Initialize return code. */
1457
1458     if (dest == DEST_P) {               /* SET DESTINATION PRINTER */
1459 #ifdef DYNAMIC
1460         if (!dsbuf)
1461           if ((dsbuf = malloc(DSBUFL+1)) == NULL)
1462             fatal("gtattr: no memory for dsbuf");
1463 #endif /* DYNAMIC */
1464         dsbuf[0] = 'P';
1465         dsbuf[1] = '\0';
1466         yy->disp.val = dsbuf;
1467         yy->disp.len = 1;
1468     }
1469     while (c = *s++) {                  /* Get attribute tag */
1470         aln = xunchar(*s++);            /* Length of attribute string */
1471         switch (c) {
1472           case '!':                     /* File length in K */
1473             for (i = 0; (i < aln) && (i < ABUFL); i++) /* Copy it */
1474               abuf[i] = *s++;
1475             abuf[i] = '\0';             /* Terminate with null */
1476             if (i < aln) s += (aln - i); /* If field was too long for buffer */
1477             yy->lengthk = atol(abuf);   /* Convert to number */
1478             break;
1479
1480           case '/':                     /* Record format */
1481             rfbuf[1] = NUL;
1482             rfbuf[2] = NUL;
1483             for (i = 0; (i < aln) && (i < RFBUFL); i++) /* Copy it */
1484               rfbuf[i] = *s++;
1485             rfbuf[i] = NUL;             /* Terminate with null */
1486             yy->recfm.val = rfbuf;      /* Pointer to string */
1487             yy->recfm.len = i;          /* Length of string */
1488             if ((rfbuf[0] != 'A') ||
1489                 (rfbuf[1] && rfbuf[1] != 'M') ||
1490                 (rfbuf[2] && rfbuf[2] != 'J')) {
1491                 debug(F110,"gattr bad recfm",rfbuf,0);
1492                 *rp++ = c;
1493                 retcode = -1;
1494             }
1495             break;
1496
1497           case '"':                     /* File type (text, binary, ...) */
1498             for (i = 0; (i < aln) && (i < FTBUFL); i++)
1499               ftbuf[i] = *s++;          /* Copy it into a static string */
1500             ftbuf[i] = '\0';
1501             if (i < aln) s += (aln - i);
1502             /* TYPE attribute is enabled? */
1503             if (attypi) {
1504                 yy->type.val = ftbuf;   /* Pointer to string */
1505                 yy->type.len = i;       /* Length of string */
1506                 debug(F111,"gattr file type", ftbuf, i);
1507                 debug(F101,"gattr binary 1","",binary);
1508                 /* Unknown type? */
1509                 if ((*ftbuf != 'A' && *ftbuf != 'B' && *ftbuf != 'I')
1510 #ifdef CK_LABELED
1511 /* ... Or our FILE TYPE is LABELED and the incoming file is text... */
1512                     || (binary == XYFT_L && *ftbuf == 'A' && !xflg)
1513 #endif /* CK_LABELED */
1514                     ) {
1515                     retcode = -1;       /* Reject the file */
1516                     *rp++ = c;
1517                     if (!opnerr) tlog(F100," refused: type","",0);
1518                     break;
1519                 }
1520 /*
1521   The following code moved here from opena() so we set binary mode
1522   as soon as requested by the attribute packet.  That way when the first
1523   data packet comes, the mode of transfer can be displayed correctly
1524   before opena() is called.
1525 */
1526                 if (yy->type.val[0] == 'A') { /* Check received attributes. */
1527 #ifdef VMS
1528                     if (binary != XYFT_I) /* VMS IMAGE overrides this */
1529 #endif /* VMS */
1530                       binary = XYFT_T;  /* Set current type to Text. */
1531                     debug(F101,"gattr binary 2","",binary);
1532                 } else if (yy->type.val[0] == 'B') {
1533 #ifdef CK_LABELED
1534                     if (binary != XYFT_L
1535 #ifdef VMS
1536                         && binary != XYFT_U /* VMS special case */
1537 #endif /* VMS */
1538                         )
1539 #endif /* CK_LABELED */
1540 #ifdef MAC
1541                     if (binary != XYFT_M) /* If not MacBinary... */
1542 #endif /* MAC */
1543                       binary = XYFT_B;
1544                     debug(F101,"gattr binary 3","",binary);
1545                 }
1546             }
1547             break;
1548
1549           case '#':                     /* File creation date */
1550             for (i = 0; (i < aln) && (i < DTBUFL); i++)
1551               dtbuf[i] = *s++;          /* Copy it into a static string */
1552             if (i < aln) s += (aln - i);
1553             dtbuf[i] = '\0';
1554             if (atdati && !xflg) {      /* Real file and dates enabled */
1555                 yy->date.val = dtbuf;   /* Pointer to string */
1556                 yy->date.len = i;       /* Length of string */
1557                 if (fncact == XYFX_U) { /* Receiving in update mode? */
1558                     if (zstime(ff,yy,1) > 0) { /* Compare dates */
1559                         *rp++ = c;      /* Discard if older, reason = date. */
1560                         if (!opnerr) tlog(F100," refused: date","",0);
1561                         retcode = -1;   /* Rejection notice. */
1562                     }
1563                 }
1564             }
1565             break;
1566
1567           case '(':                     /* File Block Size */
1568             for (i = 0; (i < aln) && (i < ABUFL); i++) /* Copy it */
1569               abuf[i] = *s++;
1570             abuf[i] = '\0';             /* Terminate with null */
1571             if (i < aln) s += (aln - i);
1572             if (atblki)
1573               yy->blksize = atol(abuf); /* Convert to number */
1574             break;
1575
1576           case '*':                     /* Encoding (transfer syntax) */
1577             for (i = 0; (i < aln) && (i < TSBUFL); i++)
1578               tsbuf[i] = *s++;          /* Copy it into a static string */
1579             if (i < aln) s += (aln - i);
1580             tsbuf[i] = '\0';
1581 #ifndef NOCSETS
1582             xlatype = XLA_NONE;         /* Assume no translation */
1583 #endif /* NOCSETS */
1584             if (atenci) {
1585                 char * ss;
1586                 yy->encoding.val = tsbuf; /* Pointer to string */
1587                 yy->encoding.len = i;   /* Length of string */
1588                 debug(F101,"gattr encoding",tsbuf,i);
1589                 ss = tsbuf+1;
1590                 switch (*tsbuf) {
1591 #ifndef NOCSETS
1592                   case 'A':               /* Normal, nothing special */
1593                     tcharset = TC_TRANSP; /* Transparent chars untranslated */
1594                     debug(F110,"gattr sets tcharset TC_TRANSP","A",0);
1595                     break;
1596                   case 'C':               /* Specified character set */
1597                     if (!xfrxla) {        /* But translation disabled */
1598                         tcharset = TC_TRANSP;
1599                         debug(F110,"gattr sets tcharset TC_TRANSP","C",0);
1600                         break;
1601                     }
1602 #ifdef UNICODE
1603                     if (!strcmp("I196",ss)) /* Treat I196 (UTF-8 no level) */
1604                       ss = "I190";          /* as I190 (UTF-8 Level 1) */
1605 #endif /* UNICODE */
1606                     if (!strcmp("I6/204",ss)) /* Treat "Latin-1 + Euro" */
1607                       ss = "I6/100";          /* as I6/100 (regular Latin-1) */
1608                     for (i = 0; i < ntcsets; i++) {
1609                         if (!strcmp(tcsinfo[i].designator,ss))
1610                           break;
1611                     }
1612                     debug(F101,"gattr xfer charset lookup","",i);
1613                     if (i == ntcsets) { /* If unknown character set, */
1614                         debug(F110,"gattr: xfer charset unknown",ss,0);
1615                         if (!unkcs) {   /* and SET UNKNOWN DISCARD, */
1616                             retcode = -1; /* reject the file. */
1617                             *rp++ = c;
1618                             if (!opnerr)
1619                               tlog(F100," refused: character set","",0);
1620                         }
1621                     } else {
1622                         tcharset = tcsinfo[i].code; /* it's known, use it */
1623                         debug(F101,"gattr switch tcharset","",tcharset);
1624                         debug(F101,"gattr fcharset","",fcharset);
1625                         if (r_cset == XMODE_A) { /* Automatic switching? */
1626                             if (tcharset > -1 && tcharset <= MAXTCSETS) {
1627                                 int x;
1628                                 x = axcset[tcharset];
1629                                 if (x > 0 && x <= MAXFCSETS) {
1630                                     fcharset = x;
1631                                     debug(F101,"gattr switch fcharset","",x);
1632                                 }
1633                             }
1634                         }
1635                         /* Set up translation type and function */
1636                         setxlatype(tcharset,fcharset);
1637                     }
1638                 break;
1639 #endif /* NOCSETS */
1640               default:                  /* Something else. */
1641                 debug(F110,"gattr unk encoding attribute",tsbuf,0);
1642                 if (!unkcs) {           /* If SET UNK DISC */
1643                     retcode = -1;
1644                     *rp++ = c;
1645                     if (!opnerr) tlog(F100," refused: encoding","",0);
1646                 }
1647                 break;
1648                 }
1649             }
1650             break;
1651
1652           case '+':                     /* Disposition */
1653 #ifdef DYNAMIC
1654             if (!dsbuf)
1655               if ((dsbuf = malloc(DSBUFL+1)) == NULL)
1656                 fatal("gtattr: no memory for dsbuf");
1657 #endif /* DYNAMIC */
1658             for (i = 0; (i < aln) && (i < DSBUFL); i++)
1659               dsbuf[i] = *s++;          /* Copy it into a separate string */
1660             dsbuf[i] = '\0';
1661             if (i < aln) s += (aln - i);
1662             rs_len = 0;
1663             if (atdisi) {               /* We are doing this attribute */
1664                 /* Copy it into the attribute structure */
1665                 yy->disp.val = dsbuf;   /* Pointer to string */
1666                 yy->disp.len = i;       /* Length of string */
1667                 d = *dsbuf;
1668 #ifndef NODISPO
1669 /*
1670   Define NODISPO to disable receipt of mail or print files and of RESEND.
1671 */
1672                 if (
1673 #ifndef datageneral                     /* MAIL supported only for */
1674 #ifndef OS2                             /* UNIX, VMS, and OS-9 */
1675 #ifndef MAC
1676 #ifndef GEMDOS
1677 #ifndef AMIGA
1678                     d != 'M' &&         /* MAIL */
1679 #endif /* AMIGA */
1680 #endif /* GEMDOS */
1681 #endif /* MAC */
1682 #endif /* OS/2 */
1683 #endif /* datageneral */
1684 #ifdef CK_RESEND
1685                     d != 'R' &&         /* RESEND */
1686 #endif /* CK_RESEND */
1687                     d != 'P') {         /* PRINT */
1688                     retcode = -1;       /* Unknown/unsupported disposition */
1689                     *rp++ = c;
1690                     if (!opnerr) tlog(F101," refused: bad disposition","",d);
1691                 }
1692                 dispos = d;
1693                 debug(F000,"gattr dispos","",dispos);
1694                 switch (d) {
1695 #ifndef NOFRILLS
1696                   case 'M':
1697                     if (!en_mai) {
1698                         retcode = -1;
1699                         *rp++ = c;
1700                         if (!opnerr) tlog(F100," refused: mail disabled","",0);
1701                         dispos = 0;
1702                     }
1703                     break;
1704 #endif /* NOFRILLS */
1705                   case 'P':
1706                     if (!en_pri) {
1707                         retcode = -1;
1708                         *rp++ = c;
1709                         if (!opnerr)
1710                           tlog(F100," refused: print disabled","",0);
1711                         dispos = 0;
1712                     }
1713                     break;
1714
1715                   case 'R':
1716                     dispos = 0;
1717 #ifdef CK_RESEND
1718                     rs_len = zgetfs(ff); /* Get length of file */
1719                     debug(F111,"gattr RESEND",ff,rs_len);
1720 #ifdef VMS
1721                     rs_len &= (long) -512; /* Ensure block boundary if VMS */
1722                     rs_len -= 512;        /* In case last block not complete */
1723                     debug(F111,"gattr rs_len",ff,rs_len);
1724 #endif /* VMS */
1725 #ifdef COMMENT
1726                     if (rs_len < 0L)    /* Local file doesn't exist */
1727                       rs_len = 0L;
1728 #endif /* COMMENT */
1729 /*
1730   Another possibility here (or later, really) would be to check if the two
1731   file lengths are the same, and if so, keep the prevailing collision action
1732   as is (note: rs_len == length of existing file; yy->length == fsize ==
1733   length of incoming file).  This could be complicated, though, since
1734   (a) we might not have received the length attribute yet, and in fact it
1735   might even be in a subsequent A-packet, yet (b) we have to accept or reject
1736   the Recover attribute now.  So better to leave as-is.  Anyway, it's probably
1737   more useful this way.
1738 */
1739                     if (rs_len > 0L) {
1740                         fncsav = fncact; /* Save collision action */
1741                         fncact = XYFX_A; /* Switch to APPEND */
1742                     }
1743 #else
1744                     retcode = -1;       /* This shouldn't happen */
1745                     *rp++ = c;          /* 'cause it wasn't negotiated. */
1746                     if (!opnerr) tlog(F100," refused: resend","",0);
1747 #endif /* CK_RESEND */
1748                 }
1749 #else  /* NODISPO */
1750                 retcode = -1;
1751                 *rp++ = c;
1752                 if (!opnerr) tlog(F100," refused: NODISPO","",0);
1753 #endif /* NODISPO */
1754             }
1755             break;
1756
1757           case '.':                     /* Sender's system ID */
1758             for (i = 0; (i < aln) && (i < IDBUFL); i++)
1759               idbuf[i] = *s++;          /* Copy it into a static string */
1760             idbuf[i] = '\0';
1761             if (i < aln) s += (aln - i);
1762             if (atsidi) {
1763                 yy->systemid.val = idbuf; /* Pointer to string */
1764                 yy->systemid.len = i;   /* Length of string */
1765             }
1766             break;
1767
1768           case '0':                     /* System-dependent parameters */
1769 #ifdef DYNAMIC
1770             if (!spbuf && !(spbuf = malloc(SPBUFL)))
1771                 fatal("gattr: no memory for spbuf");
1772 #endif /* DYNAMIC */
1773             for (i = 0; (i < aln) && (i < SPBUFL); i++)
1774               spbuf[i] = *s++;          /* Copy it into a static string */
1775             spbuf[i] = '\0';
1776             if (i < aln) s += (aln - i);
1777             if (atsysi) {
1778                 yy->sysparam.val = spbuf; /* Pointer to string */
1779                 yy->sysparam.len = i;   /* Length of string */
1780             }
1781             break;
1782
1783           case '1':                     /* File length in bytes */
1784             for (i = 0; (i < aln) && (i < ABUFL); i++) /* Copy it */
1785               abuf[i] = *s++;
1786             abuf[i] = '\0';             /* Terminate with null */
1787             if (i < aln) s += (aln - i);
1788             yy->length = atol(abuf);    /* Convert to number */
1789             debug(F111,"gattr length",abuf,(int) yy->length);
1790             break;
1791
1792
1793 #ifdef CK_PERMS
1794           case ',':                     /* System-dependent protection code */
1795             for (i = 0; (i < aln) && (i < CK_PERMLEN); i++)
1796               lprmbuf[i] = *s++;        /* Just copy it - decode later */
1797             lprmbuf[i] = '\0';          /* Terminate with null */
1798             if (i < aln) s += (aln - i);
1799             if (atlpri) {
1800                 yy->lprotect.val = (char *)lprmbuf;
1801                 yy->lprotect.len = i;
1802             } else
1803               lprmbuf[0] = NUL;
1804             break;
1805
1806           case '-':                     /* Generic "world" protection code */
1807             gprmbuf[0] = NUL;           /* Just 1 byte by definition */
1808             for (i = 0; i < aln; i++)   /* But allow for more... */
1809               if (i == 0) gprmbuf[0] = *s++;
1810             gprmbuf[1] = NUL;
1811             if (atgpri) {
1812                 yy->gprotect.val = (char *)gprmbuf;
1813                 yy->gprotect.len = gprmbuf[0] ? 1 : 0;
1814             } else
1815               gprmbuf[0] = NUL;
1816             break;
1817 #endif /* CK_PERMS */
1818
1819           default:                      /* Unknown attribute */
1820             s += aln;                   /* Just skip past it */
1821             break;
1822         }
1823     }
1824
1825     /* Check file length now, because we also need to know the file type */
1826     /* in case zchkspa() differentiates text and binary (VMS version does) */
1827
1828     if (atleni) {                       /* Length attribute enabled? */
1829         if (yy->length > -1L) {         /* Length-in-bytes attribute rec'd? */
1830             if (!zchkspa(ff,(yy->length))) { /* Check space */
1831                 retcode = -1;                /* Not enuf */
1832                 *rp++ = '1';
1833                 if (!opnerr) tlog(F100," refused: length bytes","",0);
1834             }
1835         } else if (yy->lengthk > -1L) { /* Length in K attribute rec'd? */
1836             if (!zchkspa(ff,(yy->lengthk * 1024))) {
1837                 retcode = -1;           /* Check space */
1838                 *rp++ = '!';
1839                 if (!opnerr) tlog(F100," refused: length K","",0);
1840             }
1841         }
1842     }
1843     if (yy->length > -1L) {             /* Remember the file size */
1844         fsize = yy->length;
1845     } else if (yy->lengthk > -1L) {
1846         fsize = yy->lengthk * 1024L;
1847     } else fsize = -1L;
1848
1849 #ifdef DEBUG
1850     if (deblog) {
1851         sprintf(abuf,"%ld",fsize);      /* safe */
1852         debug(F110,"gattr fsize",abuf,0);
1853     }
1854 #endif /* DEBUG */
1855
1856     if (retcode == 0) rp = rpbuf;       /* Null reply string if accepted */
1857     *rp = '\0';                         /* End of reply string */
1858
1859 #ifdef CK_RESEND
1860     if (d == 'R') {                     /* Receiving a RESEND? */
1861         debug(F101,"gattr RESEND","",retcode);
1862         /* We ignore retcodes because this overrides */
1863         if (binary != XYFT_B) {         /* Reject if not binary */
1864             retcode = -1;               /* in case type field came */
1865             ckstrncpy(rpbuf,"N+",RPBUFL); /* after the disposition field */
1866             debug(F111,"gattr RESEND not binary",rpbuf,binary);
1867         } else {                        /* Binary mode */
1868             retcode = 0;                /* Accept the file */
1869             discard = 0;                /* If SET FILE COLLISION DISCARD */
1870             sprintf(rpbuf+2,"%ld",rs_len); /* Reply with length of file */
1871             rpbuf[0] = '1';             /* '1' means Length in Bytes */
1872             rpbuf[1] = tochar((int)strlen(rpbuf+2)); /* Length of length */
1873             debug(F111,"gattr RESEND OK",rpbuf,retcode);
1874         }
1875     }
1876 #endif /* CK_RESEND */
1877     if (retcode == 0 && discard != 0) { /* Do we still have a discard flag? */
1878         ckstrncpy(rpbuf,"N?",RPBUFL);   /* Yes, must be filename collision */
1879         retcode = -1;                   /* "?" = name (reply-only code) */
1880     }
1881     yy->reply.val = rpbuf;              /* Add it to attribute structure */
1882     yy->reply.len = (int)strlen(rpbuf);
1883     if (retcode < 0) {                  /* If we are rejecting */
1884         discard = 1;                    /* remember to discard the file */
1885         rejection = rpbuf[1];           /* and use the first reason given. */
1886         if (fncsav != -1) {
1887             fncact = fncsav;
1888             fncsav = -1;
1889         }
1890     }
1891     debug(F111,"gattr return",rpbuf,retcode);
1892     return(retcode);
1893 }
1894
1895 /*  I N I T A T T R  --  Initialize file attribute structure  */
1896
1897 int
1898 initattr(yy) struct zattr *yy; {
1899     yy->lengthk = yy->length = -1L;
1900     yy->type.val = "";
1901     yy->type.len = 0;
1902     yy->date.val = "";
1903     yy->date.len = 0;
1904     yy->encoding.val = "";
1905     yy->encoding.len = 0;
1906     yy->disp.val = "";
1907     yy->disp.len = 0;
1908     yy->systemid.val = "";
1909     yy->systemid.len = 0;
1910     yy->sysparam.val = "";
1911     yy->sysparam.len = 0;
1912     yy->creator.val = "";
1913     yy->creator.len = 0;
1914     yy->account.val = "";
1915     yy->account.len = 0;
1916     yy->area.val = "";
1917     yy->area.len = 0;
1918     yy->password.val = "";
1919     yy->password.len = 0;
1920     yy->blksize = -1L;
1921     yy->xaccess.val = "";
1922     yy->xaccess.len = 0;
1923 #ifdef CK_PERMS
1924     if (!ofperms) ofperms = "";
1925     debug(F110,"initattr ofperms",ofperms,0);
1926     yy->lprotect.val = ofperms;
1927     yy->lprotect.len = 0 - strlen(ofperms); /* <-- NOTE! */
1928     /*
1929       A negative length indicates that we have a permissions string but it has
1930       been inherited from a previously existing file rather than picked up
1931       from an incoming A-packet.
1932     */
1933 #else
1934     yy->lprotect.val = "";
1935     yy->lprotect.len = 0;
1936 #endif /* CK_PERMS */
1937     yy->gprotect.val = "";
1938     yy->gprotect.len = 0;
1939     yy->recfm.val = "";
1940     yy->recfm.len = 0;
1941     yy->reply.val = "";
1942     yy->reply.len = 0;
1943 #ifdef OS2
1944     yy->longname.len = 0 ;
1945     yy->longname.val = "" ;
1946 #endif /* OS2 */
1947     return(0);
1948 }
1949
1950 /*  A D E B U -- Write attribute packet info to debug log  */
1951
1952 int
1953 adebu(f,zz) char *f; struct zattr *zz; {
1954 #ifdef DEBUG
1955     if (deblog == 0) return(0);
1956     debug(F110,"Attributes for incoming file ",f,0);
1957     debug(F101," length in K","",(int) zz->lengthk);
1958     debug(F111," file type",zz->type.val,zz->type.len);
1959     debug(F111," creation date",zz->date.val,zz->date.len);
1960     debug(F111," creator",zz->creator.val,zz->creator.len);
1961     debug(F111," account",zz->account.val,zz->account.len);
1962     debug(F111," area",zz->area.val,zz->area.len);
1963     debug(F111," password",zz->password.val,zz->password.len);
1964     debug(F101," blksize","",(int) zz->blksize);
1965     debug(F111," access",zz->xaccess.val,zz->xaccess.len);
1966     debug(F111," encoding",zz->encoding.val,zz->encoding.len);
1967     debug(F111," disposition",zz->disp.val,zz->disp.len);
1968     debug(F111," lprotection",zz->lprotect.val,zz->lprotect.len);
1969     debug(F111," gprotection",zz->gprotect.val,zz->gprotect.len);
1970     debug(F111," systemid",zz->systemid.val,zz->systemid.len);
1971     debug(F111," recfm",zz->recfm.val,zz->recfm.len);
1972     debug(F111," sysparam",zz->sysparam.val,zz->sysparam.len);
1973     debug(F101," length","",(int) zz->length);
1974     debug(F110," reply",zz->reply.val,0);
1975 #endif /* DEBUG */
1976     return(0);
1977 }
1978
1979 /*  O P E N A -- Open a file, with attributes.  */
1980 /*
1981   This function tries to open a new file to put the arriving data in.  The
1982   filename is the one in the srvcmd buffer.  File collision actions are:
1983   OVERWRITE (the existing file is overwritten), RENAME (the new file is
1984   renamed), BACKUP (the existing file is renamed), DISCARD (the new file is
1985   refused), UPDATE (the incoming file replaces the existing file only if the
1986   incoming file has a newer creation date).
1987
1988   Returns 0 on failure, nonzero on success.
1989 */
1990 extern char *rf_err;
1991
1992 int
1993 opena(f,zz) char *f; struct zattr *zz; {
1994     int x, dispos = 0;
1995     static struct filinfo fcb;          /* Must be static! */
1996
1997     debug(F110,"opena f",f,0);
1998     debug(F101,"opena discard","",discard);
1999
2000     adebu(f,zz);                        /* Write attributes to debug log */
2001
2002     ffc = 0L;                           /* Init file-character counter */
2003
2004 #ifdef PIPESEND
2005     if (pipesend)                       /* Receiving to a pipe - easy. */
2006       return(openo(f,zz,&fcb));         /* Just open the pipe. */
2007 #endif /* PIPESEND */
2008
2009     /* Receiving to a file - set up file control structure */
2010
2011     fcb.bs = fblksiz;                   /* Blocksize */
2012 #ifndef NOCSETS
2013     fcb.cs = fcharset;                  /* Character set */
2014 #else
2015     fcb.cs = 0;                         /* Character set */
2016 #endif /* NOCSETS */
2017     fcb.rl = frecl;                     /* Record Length */
2018     fcb.fmt = frecfm;                   /* Record Format */
2019     fcb.org = forg;                     /* Organization */
2020     fcb.cc = fcctrl;                    /* Carriage control */
2021     fcb.typ = binary;                   /* Type */
2022     debug(F101,"opena xflg","",xflg);
2023     debug(F101,"opena remfile","",remfile);
2024     debug(F101,"opena remappd","",remappd);
2025     if (xflg && remfile && remappd)     /* REMOTE output redirected with >> */
2026       fcb.dsp = XYFZ_A;
2027     else
2028       fcb.dsp = (fncact == XYFX_A) ? XYFZ_A : XYFZ_N; /* Disposition */
2029     debug(F101,"opena disp","",fcb.dsp);
2030     fcb.os_specific = "";               /* OS-specific info */
2031 #ifdef CK_LABELED
2032     fcb.lblopts = lf_opts;              /* Labeled file options */
2033 #else
2034     fcb.lblopts = 0;
2035 #endif /* CK_LABELED */
2036
2037     if (zz->disp.len > 0) {             /* Incoming file has a disposition? */
2038         debug(F111,"open disposition",zz->disp.val,zz->disp.len);
2039         dispos = (int) (*(zz->disp.val));
2040     }
2041     if (!dispos && xflg && remfile && remappd) /* REMOTE redirect append ? */
2042       dispos = fcb.dsp;
2043
2044     debug(F101,"opena dispos","",dispos);
2045
2046     if (!dispos) {                               /* No special disposition? */
2047         if (fncact == XYFX_B && ofn1x && ofn2) { /* File collision = BACKUP? */
2048             if (zrename(ofn1,ofn2) < 0) {        /* Rename existing file. */
2049                 debug(F110,"opena rename fails",ofn1,0);
2050                 rf_err = "Can't create backup file";
2051                 return(0);
2052             } else debug(F110,"opena rename ok",ofn2,0);
2053         }
2054     } else if (dispos == 'R') {         /* Receiving a RESEND */
2055         debug(F101,"opena remote len","",zz->length);
2056         debug(F101,"opena local len","",rs_len);
2057 #ifdef COMMENT
2058         if (fncact == XYFX_R)           /* and file collision = RENAME */
2059           if (ofn1x)
2060 #endif /* COMMENT */
2061         if (ofn1[0])
2062           f = ofn1;                     /* use original name. */
2063         if (fncact == XYFX_R)           /* if file collision is RENAME */
2064           ckstrncpy(filnam,ofn1,CKMAXPATH+1); /* restore the real name */
2065         xxscreen(SCR_AN,0,0L,f);        /* update name on screen */
2066         if (zz->length == rs_len)       /* Local and remote lengths equal? */
2067           return(-17);                  /* Secret code */
2068     }
2069     debug(F111,"opena [file]=mode: ",f,fcb.dsp);
2070     if (x = openo(f,zz,&fcb)) {         /* Try to open the file. */
2071 #ifdef pdp11
2072         tlog(F110," local name:",f,0L); /* OK, open, record local name. */
2073         makestr(&prfspec,f);            /* New preliminary name */
2074 #else
2075 #ifndef ZFNQFP
2076         tlog(F110," local name:",f,0L);
2077         makestr(&prfspec,f);
2078 #else
2079         {                               /* Log full local pathname */
2080             char *p = NULL, *q = f;
2081             if ((p = malloc(CKMAXPATH+1)))
2082               if (zfnqfp(filnam, CKMAXPATH, p))
2083                 q = p;
2084             tlog(F110," local name:",q,0L);
2085             makestr(&prfspec,q);
2086             if (p) free(p);
2087         }
2088 #endif /* ZFNQFP */
2089 #endif /* pdp11 */
2090
2091         if (binary) {                   /* Log file mode in transaction log */
2092             tlog(F101," mode: binary","",(long) binary);
2093         } else {                        /* If text mode, check character set */
2094             tlog(F100," mode: text","",0L);
2095 #ifndef NOCSETS
2096             if (xfrxla) {
2097                 if (fcharset > -1 && fcharset <= MAXFCSETS)
2098                   tlog(F110," file character-set:",fcsinfo[fcharset].name,0L);
2099                 if (tcharset > -1 && tcharset <= MAXTCSETS)
2100                   tlog(F110," xfer character-set:",tcsinfo[tcharset].name,0L);
2101             } else {
2102                   tlog(F110," character-set:","transparent",0L);
2103             }
2104 #endif /* NOCSETS */
2105             debug(F111,"opena charset",zz->encoding.val,zz->encoding.len);
2106         }
2107         debug(F101,"opena binary","",binary);
2108
2109 #ifdef COMMENT
2110         if (fsize > -1L)
2111 #endif /* COMMENT */
2112           xxscreen(SCR_FS,0,fsize,"");
2113
2114 #ifdef datageneral
2115 /*
2116   Need to turn on multi-tasking console interrupt task here, since multiple
2117   files may be received (huh?) ...
2118 */
2119         if ((local) && (!quiet))        /* Only do this if local & not quiet */
2120           consta_mt();                  /* Start the async read task */
2121 #endif /* datageneral */
2122
2123     } else {                            /* Did not open file OK. */
2124
2125         rf_err = ck_errstr();           /* Get system error message */
2126         if (*rf_err)
2127           xxscreen(SCR_EM,0,0l,rf_err);
2128         else
2129           xxscreen(SCR_EM,0,0l,"Can't open output file");
2130         tlog(F110,"Failure to open",f,0L);
2131         tlog(F110,"Error:",rf_err,0L);
2132         debug(F110,"opena error",rf_err,0);
2133     }
2134     return(x);                          /* Pass on return code from openo */
2135 }
2136
2137 /*  O P E N C  --  Open a command (in place of a file) for output */
2138
2139 int
2140 openc(n,s) int n; char * s; {
2141     int x;
2142 #ifndef NOPUSH
2143     x = zxcmd(n,s);
2144 #else
2145     x = 0;
2146 #endif /* NOPUSH */
2147     debug(F111,"openc zxcmd",s,x);
2148     o_isopen = (x > 0) ? 1 : 0;
2149     return(x);
2150 }
2151
2152 /*  C A N N E D  --  Check if current file transfer cancelled */
2153
2154 int
2155 canned(buf) CHAR *buf; {
2156     extern int interrupted;
2157     if (*buf == 'X') cxseen = 1;
2158     if (*buf == 'Z') czseen = 1;
2159     if (czseen || cxseen)
2160       interrupted = 1;
2161     debug(F101,"canned: cxseen","",cxseen);
2162     debug(F101," czseen","",czseen);
2163     return((czseen || cxseen) ? 1 : 0);
2164 }
2165
2166
2167 /*  O P E N I  --  Open an existing file for input  */
2168
2169 int
2170 openi(name) char *name; {
2171 #ifndef NOSERVER
2172     extern int fromgetpath;
2173 #endif /* NOSERVER */
2174     int x, filno;
2175     char *name2;
2176     extern CHAR *epktmsg;
2177
2178     epktmsg[0] = NUL;                   /* Initialize error message */
2179     if (memstr || sndarray) {           /* Just return if "file" is memory. */
2180         i_isopen = 1;
2181         return(1);
2182     }
2183     debug(F110,"openi name",name,0);
2184     debug(F101,"openi sndsrc","",sndsrc);
2185
2186     filno = (sndsrc == 0) ? ZSTDIO : ZIFILE;    /* ... */
2187     debug(F101,"openi file number","",filno);
2188
2189 #ifndef NOSERVER
2190     /* If I'm a server and CWD is disabled and name is not from GET-PATH... */
2191
2192     if (server && !en_cwd && !fromgetpath) {
2193         zstrip(name,&name2);
2194         if (                            /* ... check if pathname included. */
2195 #ifdef VMS
2196             zchkpath(name)
2197 #else
2198             strcmp(name,name2)
2199 #endif /* VMS */
2200             ) {
2201             tlog(F110,name,"access denied",0L);
2202             debug(F110,"openi CD disabled",name,0);
2203             ckstrncpy((char *)epktmsg,"Access denied",PKTMSGLEN);
2204             return(0);
2205         } else name = name2;
2206     }
2207 #endif /* NOSERVER */
2208
2209 #ifdef PIPESEND
2210     debug(F101,"openi pipesend","",pipesend);
2211     if (pipesend) {
2212         int x;
2213 #ifndef NOPUSH
2214         x = zxcmd(ZIFILE,name);
2215 #else
2216         x = 0;
2217 #endif /* NOPUSH */
2218         i_isopen = (x > 0) ? 1 : 0;
2219         if (!i_isopen)
2220           ckstrncpy((char *)epktmsg,"Command or pipe failure",PKTMSGLEN);
2221         debug(F111,"openi pipesend zxcmd",name,x);
2222         return(i_isopen);
2223     }
2224 #endif /* PIPESEND */
2225
2226 #ifdef CALIBRATE
2227     if (calibrate) {
2228         i_isopen = 1;
2229         return(1);
2230     }
2231 #endif /* CALIBRATE */
2232
2233     x = zopeni(filno,name);             /* Otherwise, try to open it. */
2234     debug(F111,"openi zopeni 1",name,x);
2235     if (x) {
2236         i_isopen = 1;
2237         return(1);
2238     } else {                            /* If not found, */
2239         char xname[CKMAXPATH];          /* convert the name */
2240 #ifdef NZLTOR
2241         nzrtol(name,xname,fncnv,fnrpath,CKMAXPATH);
2242 #else
2243         zrtol(name,xname);              /* to local form and then */
2244 #endif /*  NZLTOR */
2245         x = zopeni(filno,xname);        /* try opening it again. */
2246         debug(F111,"openi zopeni 2",xname,x);
2247         if (x) {
2248             i_isopen = 1;
2249             return(1);                  /* It worked. */
2250         } else {
2251             char * s;
2252             s = ck_errstr();
2253             if (s) if (!s) s = NULL;
2254             if (!s) s = "Can't open file";
2255             ckstrncpy((char *)epktmsg,s,PKTMSGLEN);
2256             tlog(F110,xname,s,0L);
2257             debug(F110,"openi failed",xname,0);
2258             debug(F110,"openi message",s,0);
2259             i_isopen = 0;
2260             return(0);
2261         }
2262     }
2263 }
2264
2265 /*  O P E N O  --  Open a new file for output.  */
2266
2267 int
2268 openo(name,zz,fcb) char *name; struct zattr *zz; struct filinfo *fcb; {
2269     char *name2;
2270 #ifdef DTILDE
2271     char *dirp;
2272 #endif /* DTILDE */
2273
2274     int channel, x;
2275
2276     if (stdouf) {                               /* Receiving to stdout? */
2277         x = zopeno(ZSTDIO,"",zz,NULL);
2278         o_isopen = (x > 0);
2279         debug(F101,"openo stdouf zopeno","",x);
2280         return(x);
2281     }
2282     debug(F110,"openo: name",name,0);
2283
2284     if (cxseen || czseen || discard) {  /* If interrupted, get out before */
2285         debug(F100," open cancelled","",0); /* destroying existing file. */
2286         return(1);                      /* Pretend to succeed. */
2287     }
2288     channel = ZOFILE;                   /* SET DESTINATION DISK or PRINTER */
2289
2290 #ifdef PIPESEND
2291     debug(F101,"openo pipesend","",pipesend);
2292     if (pipesend) {
2293         int x;
2294 #ifndef NOPUSH
2295         x = zxcmd(ZOFILE,(char *)srvcmd);
2296 #else
2297         x = 0;
2298 #endif /* NOPUSH */
2299         o_isopen = x > 0;
2300         debug(F101,"openo zxcmd","",x);
2301         return(x);
2302     }
2303 #endif /* PIPESEND */
2304
2305     if (dest == DEST_S) {               /* SET DEST SCREEN... */
2306         channel = ZCTERM;
2307         fcb = NULL;
2308     }
2309 #ifdef DTILDE
2310     if (*name == '~') {
2311         dirp = tilde_expand(name);
2312         if (*dirp) ckstrncpy(name,dirp,CKMAXPATH+1);
2313     }
2314 #endif /* DTILDE */
2315     if (server && !en_cwd) {            /* If running as server */
2316         zstrip(name,&name2);            /* and CWD is disabled, */
2317         if (strcmp(name,name2)) {       /* check if pathname was included. */
2318             tlog(F110,name,"authorization failure",0L);
2319             debug(F110,"openo CD disabled",name,0);
2320             return(0);
2321         } else name = name2;
2322     }
2323     if (zopeno(channel,name,zz,fcb) <= 0) { /* Try to open the file */
2324         o_isopen = 0;
2325         debug(F110,"openo failed",name,0);
2326         /* tlog(F110,"Failure to open",name,0L); */
2327         return(0);
2328     } else {
2329         o_isopen = 1;
2330         debug(F110,"openo ok, name",name,0);
2331         return(1);
2332     }
2333 }
2334
2335 /*  O P E N T  --  Open the terminal for output, in place of a file  */
2336
2337 int
2338 opent(zz) struct zattr *zz; {
2339     int x;
2340     ffc = tfc = 0L;
2341     x = zopeno(ZCTERM,"",zz,NULL);
2342     debug(F101,"opent zopeno","",x);
2343     if (x >= 0) {
2344         o_isopen = 1;
2345         binary = XYFT_T;
2346     } else
2347       return(0);
2348     return(x);
2349 }
2350
2351 /*  O P E N X  --  Open nothing (incoming file to be accepted but ignored)  */
2352
2353 int
2354 ckopenx(zz) struct zattr *zz; {
2355     ffc = tfc = 0L;                     /* Reset counters */
2356     o_isopen = 1;
2357     debug(F101,"ckopenx fsize","",fsize);
2358     xxscreen(SCR_FS,0,fsize,"");        /* Let screen display know the size */
2359     return(1);
2360 }
2361
2362 /*  C L S I F  --  Close the current input file. */
2363
2364 int
2365 clsif() {
2366     extern int xferstat, success;
2367     int x = 0;
2368
2369     fcps();                     /* Calculate CPS quickly */
2370
2371 #ifdef datageneral
2372     if ((local) && (!quiet))    /* Only do this if local & not quiet */
2373       if (nfils < 1)            /* More files to send ... leave it on! */
2374         connoi_mt();
2375 #endif /* datageneral */
2376
2377     debug(F101,"clsif i_isopen","",i_isopen);
2378     if (i_isopen) {                     /* If input file is open... */
2379         if (memstr) {                   /* If input was memory string, */
2380             memstr = 0;                 /* indicate no more. */
2381         } else {
2382             x = zclose(ZIFILE);         /* else close input file. */
2383         }
2384 #ifdef DEBUG
2385         if (deblog) {
2386             debug(F101,"clsif zclose","",x);
2387             debug(F101,"clsif success","",success);
2388             debug(F101,"clsif xferstat","",xferstat);
2389             debug(F101,"clsif fsize","",fsize);
2390             debug(F101,"clsif ffc","",ffc);
2391             debug(F101,"clsif cxseen","",cxseen);
2392             debug(F101,"clsif czseen","",czseen);
2393             debug(F101,"clsif discard","",czseen);
2394         }
2395 #endif /* DEBUG */
2396         if ((cxseen || czseen) && !epktsent) { /* If interrupted */
2397             xxscreen(SCR_ST,ST_INT,0l,""); /* say so */
2398 #ifdef TLOG
2399             if (tralog && !tlogfmt)
2400               doxlog(what,psfspec,fsize,binary,1,"Interrupted");
2401 #endif /* TLOG */
2402         } else if (discard && !epktsent) { /* If I'm refusing */
2403             xxscreen(SCR_ST,ST_REFU,0l,refused); /* say why */
2404 #ifdef TLOG
2405             if (tralog && !tlogfmt) {
2406                 char buf[128];
2407                 ckmakmsg(buf,128,"Refused: ",refused,NULL,NULL);
2408                 doxlog(what,psfspec,fsize,binary,1,buf);
2409             }
2410 #endif /* TLOG */
2411         } else if (!epktrcvd && !epktsent && !cxseen && !czseen) {
2412             long zz;
2413             zz = ffc;
2414 #ifdef CK_RESEND
2415             if (sendmode == SM_RESEND || sendmode == SM_PSEND)
2416               zz += sendstart;
2417 #endif /* CK_RESEND */
2418             debug(F101,"clsif fstats","",zz);
2419             fstats();                   /* Update statistics */
2420             if (                        /* Was the whole file sent? */
2421 #ifdef VMS
2422                 0                       /* Not a reliable check in VMS */
2423 #else
2424 #ifdef STRATUS
2425                 0                       /* Probably not for VOS either */
2426 #else
2427                 zz < fsize
2428 #ifdef CK_CTRLZ
2429                 && ((eofmethod != XYEOF_Z && !binary) || binary)
2430 #endif /* CK_CTRLZ */
2431 #endif /* STRATUS */
2432 #endif /* VMS */
2433                 ) {
2434                 xxscreen(SCR_ST,ST_INT,0l,"");
2435 #ifdef TLOG
2436                 if (tralog && !tlogfmt)
2437                   doxlog(what,psfspec,fsize,binary,1,"Incomplete");
2438 #endif /* TLOG */
2439             } else {
2440 #ifdef COMMENT
2441                 /* Not yet -- we don't have confirmation from the receiver */
2442                 xxscreen(SCR_ST,ST_OK,0l,"");
2443 #endif /* COMMENT */
2444 #ifdef TLOG
2445                 if (tralog && !tlogfmt)
2446                   doxlog(what,psfspec,fsize,binary,0,"");
2447 #endif /* TLOG */
2448             }
2449         }
2450     }
2451     i_isopen = 0;
2452     hcflg = 0;                          /* Reset flags */
2453     sendstart = 0L;                     /* Don't do this again! */
2454 #ifdef COMMENT
2455 /*
2456   This prevents a subsequent call to clsof() from deleting the file
2457   when given the discard flag.
2458 */
2459     *filnam = '\0';                     /* and current file name */
2460 #endif /* COMMENT */
2461     return(x);
2462 }
2463
2464
2465 /*  C L S O F  --  Close an output file.  */
2466
2467 /*  Call with disp != 0 if file is to be discarded.  */
2468 /*  Returns -1 upon failure to close, 0 or greater on success. */
2469
2470 int
2471 clsof(disp) int disp; {
2472     int x = 0;
2473     extern int success;
2474
2475     fcps();                             /* Calculate CPS quickly */
2476
2477     debug(F101,"clsof disp","",disp);
2478     debug(F101,"clsof cxseen","",cxseen);
2479     debug(F101,"clsof success","",success);
2480
2481     debug(F101,"clsof o_isopen","",o_isopen);
2482     if (fncsav != -1) {                 /* Saved file collision action... */
2483         fncact = fncsav;                /* Restore it. */
2484         fncsav = -1;                    /* Unsave it. */
2485     }
2486 #ifdef datageneral
2487     if ((local) && (!quiet))            /* Only do this if local & not quiet */
2488       connoi_mt();
2489 #endif /* datageneral */
2490     if (o_isopen && !calibrate) {
2491         if ((x = zclose(ZOFILE)) < 0) { /* Try to close the file */
2492             tlog(F100,"Failure to close",filnam,0L);
2493             xxscreen(SCR_ST,ST_ERR,0l,"Can't close file");
2494 #ifdef TLOG
2495             if (tralog && !tlogfmt)
2496               doxlog(what,prfspec,fsize,binary,1,"Can't close file");
2497 #endif /* TLOG */
2498         } else if (disp) {              /* Interrupted or refused */
2499             if (keep == 0 ||            /* If not keeping incomplete files */
2500                 (keep == SET_AUTO && binary == XYFT_T)
2501                 ) {
2502                 if (*filnam && (what & W_RECV)) /* AND we're receiving */
2503                   zdelet(filnam);       /* ONLY THEN, delete it */
2504                 if (what & W_KERMIT) {
2505                     debug(F100,"clsof incomplete discarded","",0);
2506                     tlog(F100," incomplete: discarded","",0L);
2507                     if (!epktrcvd && !epktsent) {
2508                         xxscreen(SCR_ST,ST_DISC,0l,"");
2509 #ifdef TLOG
2510                         if (tralog && !tlogfmt)
2511                           doxlog(what,prfspec,fsize,binary,1,"Discarded");
2512 #endif /* TLOG */
2513                     }
2514                 }
2515             } else {                    /* Keep incomplete copy */
2516                 debug(F100,"clsof fstats 1","",0);
2517                 fstats();
2518                 if (!discard) {  /* Unless discarding for other reason... */
2519                     if (what & W_KERMIT) {
2520                         debug(F100,"closf incomplete kept","",0);
2521                         tlog(F100," incomplete: kept","",0L);
2522                     }
2523                 }
2524                 if (what & W_KERMIT) {
2525                     if (!epktrcvd && !epktsent) {
2526                         xxscreen(SCR_ST,ST_INC,0l,"");
2527 #ifdef TLOG
2528                         if (tralog && !tlogfmt)
2529                           doxlog(what,prfspec,fsize,binary,1,"Incomplete");
2530 #endif /* TLOG */
2531                     }
2532                 }
2533             }
2534         }
2535     }
2536     if (o_isopen && x > -1 && !disp) {
2537         debug(F110,"clsof OK",rfspec,0);
2538         makestr(&rfspec,prfspec);
2539         makestr(&rrfspec,prrfspec);
2540         fstats();
2541         if (!epktrcvd && !epktsent && !cxseen && !czseen) {
2542             xxscreen(SCR_ST,ST_OK,0L,"");
2543 #ifdef TLOG
2544             if (tralog && !tlogfmt)
2545               doxlog(what,rfspec,fsize,binary,0,"");
2546 #endif /* TLOG */
2547         }
2548     }
2549     rs_len = 0;
2550     o_isopen = 0;                       /* The file is not open any more. */
2551     cxseen = 0;                         /* Reset per-file interruption flag */
2552     return(x);                          /* Send back zclose() return code. */
2553 }
2554
2555 #ifdef SUNOS4S5
2556 tolower(c) char c; { return((c)-'A'+'a'); }
2557 toupper(c) char c; { return((c)-'a'+'A'); }
2558 #endif /* SUNOS4S5 */
2559 #endif /* NOXFER */