BSD 4_4_Lite2 development
authorCSRG <csrg@ucbvax.Berkeley.EDU>
Sun, 3 Jul 1994 08:11:41 +0000 (00:11 -0800)
committerCSRG <csrg@ucbvax.Berkeley.EDU>
Sun, 3 Jul 1994 08:11:41 +0000 (00:11 -0800)
Work on file usr/src/contrib/kermit-5A.188/ckcfn3.c

Synthesized-from: CSRG/cd3/4.4BSD-Lite2

usr/src/contrib/kermit-5A.188/ckcfn3.c [new file with mode: 0644]

diff --git a/usr/src/contrib/kermit-5A.188/ckcfn3.c b/usr/src/contrib/kermit-5A.188/ckcfn3.c
new file mode 100644 (file)
index 0000000..66cab5b
--- /dev/null
@@ -0,0 +1,1437 @@
+/*  C K C F N 3  --  Packet buffer management for C-Kermit  */
+
+/* (plus assorted functions tacked on at the end) */
+
+/*
+  Author: Frank da Cruz (fdc@columbia.edu, FDCCU@CUVMA.BITNET),
+  Columbia University Center for Computing Activities.
+  First released January 1985.
+  Copyright (C) 1985, 1992, Trustees of Columbia University in the City of New
+  York.  Permission is granted to any individual or institution to use this
+  software as long as it is not sold for profit.  This copyright notice must be
+  retained.  This software may not be included in commercial products without
+  written permission of Columbia University.
+*/
+
+#include "ckcdeb.h"
+#include "ckcasc.h"
+#include "ckcker.h"
+#include "ckcxla.h"
+
+extern int unkcs, wmax, wcur, discard, bctu, bctl, local, fdispla;
+extern CHAR *data;
+extern char filnam[];
+#ifndef NOFRILLS
+extern int rprintf, rmailf;            /* REMOTE MAIL, PRINT */
+extern char optbuf[];                  /* Options buffer for mail, print */
+#endif /* NOFRILLS */
+extern int wslots;
+extern int fblksiz, frecl, forg, frecfm, fncact, fcctrl, lf_opts;
+#ifdef DYNAMIC
+  extern CHAR *srvcmd;
+#else
+  extern CHAR srvcmd[];
+#endif
+
+extern int binary, spsiz;
+extern int pktnum, cxseen, czseen, bsave, bsavef, nfils, stdinf;
+extern int memstr, stdouf, keep, sndsrc, hcflg;
+extern int server, en_cwd;
+
+extern int
+  atenci, atenco, atdati, atdato, atleni, atleno, atblki, atblko,
+  attypi, attypo, atsidi, atsido, atsysi, atsyso, atdisi, atdiso; 
+
+#ifdef datageneral
+extern int quiet;
+#endif /* datageneral */
+
+extern long fsize, filcnt, ffc, tfc;
+
+#ifndef NOCSETS
+extern int tcharset, fcharset;
+extern int ntcsets;
+extern struct csinfo tcsinfo[], fcsinfo[];
+
+/* Pointers to translation functions */
+#ifdef CK_ANSIC
+extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])(CHAR); /* Character set */
+extern CHAR (*xlr[MAXTCSETS+1][MAXFCSETS+1])(CHAR); /* translation functions */
+extern CHAR (*rx)(CHAR); /* Pointer to input character translation function */
+extern CHAR (*sx)(CHAR); /* Pointer to output character translation function */
+#else
+extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])();        /* Character set */
+extern CHAR (*xlr[MAXTCSETS+1][MAXFCSETS+1])();        /* translation functions. */
+extern CHAR (*rx)();   /* Pointer to input character translation function */
+extern CHAR (*sx)();    /* Pointer to output character translation function */
+#endif /* CK_ANSIC */
+#endif /* NOCSETS */
+
+/* Variables global to Kermit that are defined in this module */
+
+int winlo;                             /* packet number at low window edge  */
+
+int sbufnum;                           /* number of free buffers */
+int dum001 = 1234;                     /* protection... */
+int sbufuse[MAXWS];                    /* buffer in-use flag */
+int dum003 = 1111;
+int rbufnum;                           /* number of free buffers */
+int dum002 = 4321;                     /* more protection */
+int rbufuse[MAXWS];                    /* buffer in-use flag */
+int sseqtbl[64];                       /* sequence # to buffer # table */
+int rseqtbl[64];                       /* sequence # to buffer # table */
+int sacktbl[64];                       /* sequence # ack table */
+
+#ifdef DYNAMIC
+struct pktinfo *s_pkt = NULL;          /* array of pktinfo structures */
+struct pktinfo *r_pkt = NULL;          /* array of pktinfo structures */
+#else
+struct pktinfo s_pkt[MAXWS];           /* array of pktinfo structures */
+struct pktinfo r_pkt[MAXWS];           /* array of pktinfo structures */
+#endif /* DYNAMIC */
+
+#ifdef DEBUG
+char xbuf[200];                                /* For debug logging */
+#endif /* DEBUG */
+
+#ifdef DYNAMIC
+CHAR *bigsbuf = NULL, *bigrbuf = NULL;
+#else
+char bigsbt[8];                                /* Protection (shouldn't need this). */
+                                       /* BUT DON'T REMOVE IT! */
+CHAR bigsbuf[SBSIZ + 5];               /* Send-packet buffer area */
+char bigrbt[8];                                /* Safety padding */
+CHAR bigrbuf[RBSIZ + 5];               /* Receive-packet area */
+#endif
+int bigsbsiz = SBSIZ;                  /* Sizes of big send & rcv buffers. */
+int bigrbsiz = RBSIZ;
+
+/* FUNCTIONS */
+
+/* For sanity, use "i" for buffer slots, "n" for packet numbers. */
+
+/* I N I B U F S */
+
+/*
+  Allocates the big send and receive buffers.
+  Call with size for big send buffer (s) and receive buffer (r).
+  These sizes can be different.
+  Attempts to allocate buffers of the requested size, but if it can't,
+  it will allocate smaller ones.
+  Sets global variables bigsbsiz and bigrbsiz to the actual sizes,
+  and bigsbuf and bigrbuf pointing to the actual buffers.
+  Designed to be called more than once.
+  Returns 0 on success, -1 on failure.
+*/
+
+CHAR *bigbufp = NULL;
+
+int
+inibufs(s,r) int s, r; {
+#ifdef DYNAMIC
+    int size, x;
+    long z;
+
+    if (s < 80 || r < 80) return(-1);  /* Validate arguments. */
+
+    if (!s_pkt) {                      /* Allocate packet info structures */
+       if (!(s_pkt = (struct pktinfo *) malloc(sizeof(struct pktinfo)*MAXWS)))
+         fatal("ini_pkts: no memory for s_pkt");
+    }
+    for (x = 0; x < MAXWS; x++)
+      s_pkt[x].pk_adr = NULL;          /* Initialize addresses */
+
+    if (!r_pkt) {
+       if (!(r_pkt = (struct pktinfo *) malloc(sizeof(struct pktinfo)*MAXWS)))
+         fatal("ini_pkts: no memory for s_pkt");
+    }
+    for (x = 0; x < MAXWS; x++)
+      r_pkt[x].pk_adr = NULL;          /* Initialize addresses */
+
+    if (!srvcmd) {                     /* Allocate srvcmd buffer */
+       srvcmd = (CHAR *) malloc(r + 100);
+       if (!srvcmd) return(-1);
+    }
+    if (bigbufp) {                     /* Free previous buffers, if any. */
+       free(bigbufp);
+       bigbufp = NULL;
+    }
+    size = s + r + 40;                 /* Combined requested size + padding */
+
+    /* Try to get the space.  If malloc fails, try to get a little less. */
+    /* (Obviously, this algorithm can be refined.) */
+
+    while (!(bigbufp = (CHAR *) malloc(size))) {
+       size = (size * 3) / 2;          /* Failed, cut size by 1/3. */
+       if (size < 200)                 /* Try again until too small. */
+         return(-1);
+    }
+    debug(F101,"inibufs size","",size);        /* OK, we got some space. */
+
+/*
+  Now divide the allocated space between the send and receive buffers in the
+  requested proportion.  The natural formula would be (s / (s + r)) * size
+  (for the send buffer), but that doesn't work with integer arithmetic and we
+  can't use floating point because some machines don't have it.  This can be
+  rearranged as (s * size) / (s + r).  But (s * size) can be VERY large, too
+  large for 32 bits.  So let's do it this way.  This arithmetic works for
+  buffer sizes up to about 5,000,000.
+*/
+#define FACTOR 20L
+    z = ( (long) s * FACTOR ) / ( (long) s + (long) r );
+    x = ( z * ( (long) size / FACTOR ) );
+    if (x < 0) return(-1);             /* Catch overflow */
+
+    bigsbsiz = x - 5;                  /* Size of send buffer */
+    bigsbuf = bigbufp;                 /* Address of send buffer */
+    debug(F101,"inibufs bigsbsiz","",bigsbsiz);
+
+    bigrbsiz = size - x - 5;           /* Size of receive buffer */
+    bigrbuf = bigbufp + x;             /* Addresss of receive buffer */
+    debug(F101,"inibufs bigrbsiz","",bigrbsiz);
+
+    return(0);                         /* Success */
+#else                                  /* No dynamic allocation */
+    bigsbsiz = SBSIZ;                  /* Just use the symbols */
+    bigrbsiz = RBSIZ;                  /* ... */
+    return(0);                         /* Success. */
+#endif /* DYNAMIC */
+}
+
+
+/* M A K E B U F  --  Makes and clears a new buffers.  */
+
+/* Call with: */
+/*  slots:  number of buffer slots to make, 1 to 31 */
+/*  bufsiz: size of the big buffer */
+/*  buf:    address of the big buffer */
+/*  xx:     pointer to array of pktinfo structures for these buffers */
+
+/* Subdivides the big buffer into "slots" buffers. */
+
+/* Returns: */
+/*  -1 if too many or too few slots requested,     */
+/*  -2 if slots would be too small.      */
+/*   n (positive) on success = size of one buffer. */
+/*   with pktinfo structure initialized for this set of buffers. */
+
+int
+makebuf(slots,bufsiz,buf,xx)
+/* makebuf */ int slots, bufsiz; CHAR buf[]; struct pktinfo *xx; {
+
+    CHAR *a;
+    int i, size;
+
+    debug(F101,"makebuf","",slots);
+    debug(F101,"makebuf bufsiz","",bufsiz);
+    debug(F101,"makebuf MAXWS","",MAXWS);
+
+    if (slots > MAXWS || slots < 1) return(-1);
+    if (bufsiz < slots * 10 ) return(-2);
+
+    size = bufsiz / slots;             /* Divide up the big buffer. */
+    a = buf;                           /* Address of first piece. */
+
+    for (i = 0; i < slots; i++) {
+       struct pktinfo *x = &xx[i];
+       x->bf_adr = a;                  /* Address of this buffer */
+       x->bf_len = size;               /* Length of this buffer */
+       x->pk_len = 0;                  /* Length of data field */
+        x->pk_typ = ' ';               /* packet type */
+       x->pk_seq = -1;                 /* packet sequence number */
+        x->pk_flg = 0;                 /* ack'd bit */
+        x->pk_rtr = 0;                 /* retransmissions */
+       *a = '\0';                      /* Clear the buffer */
+       a += size;                      /* Position to next buffer slot */
+    }
+    return(size);
+}
+
+/*  M A K S B U F  --  Makes the send-packet buffer  */
+
+int
+mksbuf(slots) int slots; {
+    int i, x;
+    sbufnum = 0;
+    if ((x = makebuf(slots,bigsbsiz,bigsbuf,s_pkt)) < 0) {
+       debug(F101,"mksbuf makebuf return","",x);
+       return(x);
+    }
+    debug(F101,"mksbuf makebuf return","",x);
+    for (i = 0; i < 64; i++) {         /* Initialize sequence-number- */
+       sseqtbl[i] = -1;                /* to-buffer-number table. */
+        sacktbl[i] = 0;
+    }
+    for (i = 0; i < MAXWS; i++)
+      sbufuse[i] = 0;                  /* Mark each buffer as free */
+    sbufnum = slots;
+    return(x);
+}
+
+/*  M A K R B U F  --  Makes the receive-packet buffer  */
+
+int
+mkrbuf(slots) int slots; {
+    int i, x;
+    rbufnum = 0;
+    if ((x = makebuf(slots,bigrbsiz,bigrbuf,r_pkt)) < 0) {
+       debug(F101,"mkrbuf makebuf return","",x);
+       return(x);
+    }
+    debug(F101,"mkrbuf makebuf return","",x);
+    for (i = 0; i < 64; i++) {         /* Initialize sequence-number- */
+       rseqtbl[i] = -1;                /* to-buffer-number table. */
+    }
+    for (i = 0; i < MAXWS; i++)
+      rbufuse[i] = 0;                  /* Mark each buffer as free */
+    rbufnum = slots;
+    return(x);
+}
+
+/*  W I N D O W  --  Resize the window to n  */
+
+int
+window(n) int n; {
+    debug(F101,"window","",n);
+    if (n < 1 || n > 31) return(-1);
+    if (mksbuf(n) < 0) return(-1);
+    if (mkrbuf(n) < 0) return(-1);
+    wslots = n;
+#ifdef DEBUG
+    if (deblog) dumpsbuf();
+    if (deblog) dumprbuf();
+#endif /* DEBUG */
+    return(0);
+}
+
+/*  G E T S B U F  --  Allocate a send-buffer.  */
+
+/*  Call with packet sequence number to allocate buffer for. */
+/*  Returns: */
+/*   -4 if argument is invalid (negative, or greater than 63) */
+/*   -3 if buffers were thought to be available but really weren't (bug!) */
+/*   -2 if the number of free buffers is negative (bug!) */
+/*   -1 if no free buffers. */
+/*   0 or positive, packet sequence number, with buffer allocated for it. */
+
+int
+getsbuf(n) int n; {                    /* Allocate a send-buffer */
+    int i;
+    if (n < 0 || n > 63) {
+       debug(F101,"getsbuf bad arg","",n);
+       return(-4);     /* Bad argument */
+    }
+    debug(F101,"getsbuf, packet","",n);
+    debug(F101,"getsbuf, sbufnum","",sbufnum);
+    if (sbufnum == 0) return(-1);      /* No free buffers. */
+    if (sbufnum < 0) return(-2);       /* Shouldn't happen. */
+    for (i = 0; i < wslots; i++)       /* Find the first one not in use. */
+      if (sbufuse[i] == 0) {           /* Got one? */
+         sbufuse[i] = 1;               /* Mark it as in use. */
+         sbufnum--;                    /* One less free buffer. */
+         *s_pkt[i].bf_adr = '\0';      /* Zero the buffer data field */
+         s_pkt[i].pk_seq = n;          /* Put in the sequence number */
+          sseqtbl[n] = i;              /* Back pointer from sequence number */
+          sacktbl[n] = 0;              /* ACK flag */
+         s_pkt[i].pk_len = 0;          /* Data field length now zero. */
+         s_pkt[i].pk_typ = ' ';        /* Blank the packet type too. */
+         s_pkt[i].pk_flg = 0;          /* Zero the flags */
+         s_pkt[i].pk_rtr = 0;          /* Zero the retransmission count */
+         data = s_pkt[i].bf_adr + 7;   /* Set global "data" address. */
+         wcur = wslots - sbufnum;      /* Current number of window slots */
+         if (i+1 > wmax) wmax = i+1;   /* For statistics. */
+         return(n);                    /* Return its index. */
+      }
+    sbufnum = 0;                       /* Didn't find one. */
+    return(-3);                                /* Shouldn't happen! */
+}
+
+int
+getrbuf() {                            /* Allocate a receive buffer */
+    int i;
+    debug(F101,"getrbuf rbufnum","",rbufnum);
+    debug(F101,"getrbuf wslots","",wslots);
+    debug(F101,"getrbuf dum002","",dum002);
+    debug(F101,"getrbuf dum003","",dum003);
+    if (rbufnum == 0) return(-1);      /* No free buffers. */
+    if (rbufnum < 0) return(-2);       /* Shouldn't happen. */
+    for (i = 0; i < wslots; i++)       /* Find the first one not in use. */
+      if (rbufuse[i] == 0) {           /* Got one? */
+         rbufuse[i] = 1;               /* Mark it as in use. */
+         *r_pkt[i].bf_adr = '\0';      /* Zero the buffer data field */
+         rbufnum--;                    /* One less free buffer. */
+         debug(F101,"getrbuf new rbufnum","",rbufnum);
+#ifdef COMMENT
+         wcur = wslots - rbufnum;      /* Current number of window slots */
+#endif /* COMMENT */
+         if (i+1 > wmax) wmax = i+1;   /* For statistics. */
+         return(i);                    /* Return its index. */
+      }
+    debug(F101,"getrbuf foulup","",i);
+    rbufnum = 0;                       /* Didn't find one. */
+    return(-3);                                /* Shouldn't happen! */
+}
+
+/*  F R E E S B U F  --  Free send-buffer for given packet sequence number */
+
+/*  Returns:  */
+/*   1 upon success  */
+/*  -1 if specified buffer does not exist */
+
+int
+freesbuf(n) int n; {                   /* Release send-buffer for packet n. */
+    int i;
+
+    debug(F101,"freesbuf","",n);
+    if (n < 0 || n > 63)               /* No such packet. */
+      return(-1);
+    i = sseqtbl[n];                    /* Get the window slot number. */
+    if (i > -1 && i <= wslots) {
+       sseqtbl[n] = -1;                /* If valid, remove from seqtbl */
+       sbufnum++;                      /* and count one more free buffer */
+       sbufuse[i] = 0;                 /* and mark it as free, */
+    } else {
+       debug(F101," sseqtbl[n]","",sseqtbl[n]);
+       return(-1);
+    }
+
+/* The following is done only so dumped buffers will look right. */
+
+    if (1) {
+       *s_pkt[i].bf_adr = '\0';        /* Zero the buffer data field */
+       s_pkt[i].pk_seq = -1;           /* Invalidate the sequence number */
+       s_pkt[i].pk_len = 0;            /* Data field length now zero. */
+       s_pkt[i].pk_typ = ' ';          /* Blank the packet type too. */
+       s_pkt[i].pk_flg = 0;            /* And zero the flag */
+       s_pkt[i].pk_rtr = 0;            /* And the retries field. */
+    }
+    return(1);
+}
+
+int
+freerbuf(i) int i; {                   /* Release receive-buffer slot "i". */
+    int n;
+
+/* NOTE !! Currently, this function frees the indicated buffer, but */
+/* does NOT erase the data.  The program counts on this.  Will find a */
+/* better way later.... */
+
+    debug(F101,"freerbuf, slot","",i);
+    if (i < 0 || i >= wslots) {                /* No such slot. */
+       debug(F101,"freerbuf no such slot","",i);
+       return(-1);
+    }
+    n = r_pkt[i].pk_seq;               /* Get the packet sequence number */
+    debug(F101,"freerbuf, packet","",n);
+    if (n > -1 && n < 64)              /* If valid, remove from seqtbl */
+      rseqtbl[n] = -1;
+    if (rbufuse[i] != 0) {             /* If really allocated, */
+       rbufuse[i] = 0;                 /* mark it as free, */
+       rbufnum++;                      /* and count one more free buffer. */
+       debug(F101,"freerbuf, new rbufnum","",rbufnum);
+    }
+
+/* The following is done only so dumped buffers will look right. */
+
+    if (1) {
+     /* *r_pkt[i].bf_adr = '\0'; */    /* Zero the buffer data field */
+       r_pkt[i].pk_seq = -1;           /* And from packet list */
+       r_pkt[i].pk_len = 0;            /* Data field length now zero. */
+       r_pkt[i].pk_typ = ' ';          /* Blank the packet type too. */
+       r_pkt[i].pk_flg = 0;            /* And zero the flag */
+       r_pkt[i].pk_rtr = 0;            /* And the retries field. */
+    }
+    return(1);
+}
+
+/* This is like freerbuf, except it's called with a packet sequence number */
+/* rather than a packet buffer index. */
+
+VOID
+freerpkt(seq) int seq; {
+    int k;
+    debug(F101,"freerpkt seq","",seq);
+    k = rseqtbl[seq];
+    debug(F101,"freerpkt k","",k);
+    if (k > -1) {
+       k = freerbuf(k);
+       debug(F101,"freerpkt freerbuf","",k);
+    }
+}
+
+
+/*  C H K W I N  --  Check if packet n is in window. */
+
+/*  Returns: */
+/*    0 if it is in the current window,  */
+/*   +1 if it would have been in previous window (e.g. if ack was lost), */
+/*   -1 if it is outside any window (protocol error),   */
+/*   -2 if either of the argument packet numbers is out of range.  */
+
+/* Call with packet number to check (n), lowest packet number in window */
+/* (bottom), and number of slots in window (slots).  */
+
+int
+chkwin(n,bottom,slots) int n, bottom, slots; {
+    int top, prev;
+
+    debug(F101,"chkwin packet","",n);
+    debug(F101,"chkwin winlo","",bottom);
+    debug(F101,"chkwin slots","",slots);
+
+/* First do the easy and common cases, where the windows are not split. */
+
+    if (n < 0 || n > 63 || bottom < 0 || bottom > 63)
+      return(-2);
+
+    if (n == bottom) return(0);                /* In a perfect world... */
+
+    top = bottom + slots;              /* Calculate window top. */
+    if (top < 64 && n < top && n >= bottom)
+      return(0);                       /* In current window. */
+
+    prev = bottom - slots;             /* Bottom of previous window. */
+    if (prev > -1 && n < bottom && n > prev)
+      return(1);                       /* In previous. */
+
+/* Now consider the case where the current window is split. */
+
+    if (top > 63) {                    /* Wraparound... */
+       top -= 64;                      /* Get modulo-64 sequence number */
+       if (n < top || n >= bottom) {
+           return(0);                  /* In current window. */
+       } else {                        /* Not in current window. */
+           if (n < bottom && n >= prev) /* Previous window can't be split. */
+             return(1);                /* In previous window. */
+           else
+             return(-1);               /* Not in previous window. */
+       }
+    }
+
+/* Now the case where current window not split, but previous window is. */ 
+
+    if (prev < 0) {                    /* Is previous window split? */
+       prev += 64;                     /* Yes. */
+       if (n < bottom || n >= prev)
+         return(1);                    /* In previous window. */
+    } else {                           /* Previous window not split. */
+       if (n < bottom && n >= prev)
+         return(1);                    /* In previous window. */
+    }
+    
+/* It's not in the current window, and not in the previous window... */
+
+    return(-1);                                /* So it's not in any window. */
+}
+
+int
+dumpsbuf() {                           /* Dump send-buffers */
+#ifdef DEBUG
+    int j, x;                          /* to debug log. */
+
+    if (! deblog) return(0);
+    x = zsoutl(ZDFILE,"SEND BUFFERS:");
+    if (x < 0) {
+       deblog = 0;
+       return(0);
+    }
+    x=zsoutl(ZDFILE,"buffer inuse address length data type seq flag retries");
+    if (x < 0) {
+       deblog = 0;
+       return(0);
+    }
+    for ( j = 0; j < wslots; j++ ) {
+       sprintf(xbuf,"%4d%6d%10d%5d%6d%4c%5d%5d%6d\n",
+              j,
+              sbufuse[j],
+              s_pkt[j].bf_adr, 
+              s_pkt[j].bf_len,
+              s_pkt[j].pk_len,
+              s_pkt[j].pk_typ,
+              s_pkt[j].pk_seq,
+              s_pkt[j].pk_flg,
+              s_pkt[j].pk_rtr
+              );
+       if (zsout(ZDFILE,xbuf) < 0)  {
+           deblog = 0;
+           return(0);
+       }
+       if (s_pkt[j].pk_adr) {
+           x = (int)strlen((char *) s_pkt[j].pk_adr);
+           if (x)
+             sprintf(xbuf,"[%.72s%s]\n",s_pkt[j].pk_adr, x > 72 ? "..." : "");
+           else
+             sprintf(xbuf,"[(empty string)]\n");
+       } else {
+           sprintf(xbuf,"[(null pointer)]\n");
+       }
+       if (zsout(ZDFILE,xbuf) < 0) {
+           deblog = 0;
+           return(0);
+       }
+    }
+    sprintf(xbuf,"free: %d, winlo: %d\n", sbufnum, winlo);
+    if (zsout(ZDFILE,xbuf) < 0) {
+       deblog = 0;
+       return(0);
+    }
+    return(0);
+#endif /* DEBUG */
+}
+int
+dumprbuf() {                           /* Dump receive-buffers */
+#ifdef DEBUG
+    int j, x;
+    if (! deblog) return(0);
+    if (zsoutl(ZDFILE,"RECEIVE BUFFERS:") < 0) {
+       deblog = 0;
+       return(0);
+    }
+    x=zsoutl(ZDFILE,"buffer inuse address length data type seq flag retries");
+    if (x < 0) {
+       deblog = 0;
+       return(0);
+    }
+    for ( j = 0; j < wslots; j++ ) {
+       sprintf(xbuf,"%4d%6d%10d%5d%6d%4c%5d%5d%6d\n",
+              j,
+              rbufuse[j],
+              r_pkt[j].bf_adr, 
+              r_pkt[j].bf_len,
+              r_pkt[j].pk_len,
+              r_pkt[j].pk_typ,
+              r_pkt[j].pk_seq,
+              r_pkt[j].pk_flg,
+              r_pkt[j].pk_rtr
+              );
+       if (zsout(ZDFILE,xbuf) < 0) {
+           deblog = 0;
+           return(0);
+       }
+       x = (int)strlen((char *)r_pkt[j].bf_adr);
+       sprintf(xbuf,"[%.72s%s]\n",r_pkt[j].bf_adr, x > 72 ? "..." : "");
+       if (zsout(ZDFILE,xbuf) < 0)  {
+           deblog = 0;
+           return(0);
+       }
+    }
+    sprintf(xbuf,"free: %d, winlo: %d\n", rbufnum, winlo);
+    if (zsout(ZDFILE,xbuf) < 0)  {
+       deblog = 0;
+       return(0);
+    }
+    return(0);
+#endif /* DEBUG */
+}
+\f
+/*** Some misc functions also moved here from the other ckcfn*.c modules ***/
+/*** to even out the module sizes. ***/
+
+/* Attribute Packets. */
+
+/*
+  Call with xp == 0 if we're sending a real file (F packet),
+  or xp != 0 for screen data (X packet).
+  Returns 0 on success, -1 on failure.
+*/
+int
+sattr(xp) int xp; {                    /* Send Attributes */
+    int i, j, aln;
+    char *tp;
+    struct zattr x;
+
+    if (zsattr(&x) < 0) return(-1);    /* Get attributes or die trying */
+    if (nxtpkt() < 0) return(-1);      /* Next packet number */
+    i = 0;                             /* i = Data field character number */
+    if (atsido) {                      /* System type */
+       data[i++] = '.';
+       data[i++] = tochar(x.systemid.len); /*  Copy from attr structure */
+       for (j = 0; j < x.systemid.len; j++)
+         data[i++] = x.systemid.val[j];
+    }
+    if (attypo) {                      /* File type */
+       data[i++] = '"';
+       if (
+#ifdef VMS
+       binary == XYFT_I || binary == XYFT_L || /* IMAGE or LABELED */
+       !strncmp(x.recfm.val,"F",1)             /* or RECFM=Fxxxxxx */
+#else
+       binary                          /* User said SET FILE TYPE BINARY  */
+#endif /* VMS */
+           ) {                         /* Binary */
+           data[i++] = tochar(2);      /*  Two characters */
+           data[i++] = 'B';            /*  B for Binary */
+           data[i++] = '8';            /*  8-bit bytes (note assumption...) */
+#ifdef VMS
+           if (binary != XYFT_I && binary != XYFT_L) binary = XYFT_B;
+#endif /* VMS */
+       } else {                        /* Text */
+           data[i++] = tochar(3);      /*  Three characters */
+           data[i++] = 'A';            /*  A = (extended) ASCII with CRLFs */
+           data[i++] = 'M';            /*  M for carriage return */
+           data[i++] = 'J';            /*  J for linefeed */
+#ifdef VMS
+           binary = XYFT_T;            /* We automatically detected text */
+#endif /* VMS */
+
+#ifdef NOCSETS
+           data[i++] = '*';            /* Encoding */
+           data[i++] = tochar(1);      /* Length of value is 1 */
+           data[i++] = 'A';            /* A for ASCII */
+#else
+           if (tcharset == TC_TRANSP)  /* Transfer character set */
+             tp = NULL;
+           else
+             tp = tcsinfo[tcharset].designator;
+           if ((tp != NULL) && (aln = (int)strlen(tp)) > 0) {
+               data[i++] = '*';        /* Encoding */
+               data[i++] = tochar(aln+1); /*  Length of charset designator. */
+               data[i++] = 'C';        /*  Text in specified character set. */
+               for (j = 0; j < aln; j++) /*  Designator from tcsinfo struct */
+                 data[i++] = *tp++;      /*  Example: *&I6/100 */
+           }
+#endif /* NOCSETS */
+       }
+    }
+    if ((xp == 0) && (x.length > -1L)) { /* If it's a real file */
+
+       if (atdato && (aln = x.date.len) > 0) { /* Creation date, if any */
+           data[i++] = '#';
+           data[i++] = tochar(aln);
+           for (j = 0; j < aln; j++)
+             data[i++] = x.date.val[j];
+       }
+       if (atleno) {
+           data[i] = '!';                      /* File length in K */
+           sprintf((char *) &data[i+2],"%ld",x.lengthk);
+           aln = (int)strlen((char *)(data+i+2));
+           data[i+1] = tochar(aln);
+           i += aln + 2;
+
+           data[i] = '1';                      /* File length in bytes */
+           sprintf((char *) &data[i+2],"%ld",x.length);
+           aln = (int)strlen((char *)(data+i+2));
+           data[i+1] = tochar(aln);
+           i += aln + 2;
+       }
+       if (atblko && fblksiz) {                /* Blocksize, if set */
+           data[i] = '(';
+           sprintf((char *) &data[i+2],"%d",fblksiz);
+           aln = (int)strlen((char *)(data+i+2));
+           data[i+1] = tochar(aln);
+           i += aln + 2;
+       }
+    }
+#ifndef NOFRILLS
+    if ((rprintf || rmailf) && atdiso) { /* MAIL, or REMOTE PRINT?  */
+       data[i++] = '+';                /* Disposition */
+        data[i++] = tochar((int)strlen(optbuf) + 1); /* Options, if any */
+       if (rprintf)
+         data[i++] = 'P';              /* P for Print */
+       else
+         data[i++] = 'M';              /* M for Mail */
+       for (j = 0; optbuf[j]; j++)     /* Copy any options */
+         data[i++] = optbuf[j];
+    }
+#endif /* NOFRILLS */
+    data[i] = '\0';                    /* Make sure it's null-terminated */
+    aln = (int)strlen((char *)data);   /* Get overall length of attributes */
+
+/* Change this code to break attribute data up into multiple packets! */
+
+    j = (spsiz < 95) ? (92 - bctl) : (spsiz - 2 - bctl);
+    if (aln > j) {                     /* Check length of result */
+       spack('A',pktnum,0,(CHAR *)""); /* send an empty attribute packet */
+       debug(F101,"sattr pkt too long","",aln); /* if too long */
+       debug(F101,"sattr spsiz","",spsiz);
+    } else {                           /* Otherwise */
+       spack('A',pktnum,aln,data);     /* send the real thing. */
+       debug(F111,"sattr",data,aln);
+    }
+
+    return(0);
+}
+
+static char *refused = "";
+
+static char *reason[] = {
+    "size", "type", "date", "creator", "account", "area", "password",
+    "blocksize", "access", "encoding", "disposition", "protection",
+    "protection", "origin", "format", "sys-dependent", "size" };
+static int nreason = sizeof(reason) / sizeof(char *);
+
+char *
+getreason(s) char *s; {                        /* Decode attribute refusal reason */
+    char c, *p;
+    p = s;
+    if (*p++ != 'N') return("");       /* Should start with N */
+    if ((c = *p) > SP) {               /* get reason, */
+       c -= '!';                       /* get offset */
+       p = ((unsigned int) ((CHAR) c) <= nreason) ? reason[c] : "unknown";
+    }
+    return(p);
+}
+
+int
+rsattr(s) CHAR *s; {                   /* Read response to attribute packet */
+    debug(F111,"rsattr: ",s,*s);
+    if (*s == 'N') {                   /* If it's 'N' followed by anything, */
+       refused = getreason((char *)s); /* they are refusing, get reason. */
+       debug(F110,"refused",refused,0);
+       tlog(F110,"refused",refused,0L);
+       return(-1);     
+    } else refused = "";
+    return(0);
+}
+
+int
+gattr(s, yy) CHAR *s; struct zattr *yy; { /* Read incoming attribute packet */
+    char c;
+    int aln, i;
+#define ABUFL 40                       /* Temporary buffer for conversions */
+    char abuf[ABUFL];
+#define FTBUFL 10                      /* File type buffer */
+    static char ftbuf[FTBUFL];
+#define DTBUFL 40                      /* File creation date */
+    static char dtbuf[DTBUFL];
+#define TSBUFL 10                      /* Transfer syntax */
+    static char tsbuf[TSBUFL];
+#define IDBUFL 10                      /* System ID */
+    static char idbuf[IDBUFL];
+#ifndef DYNAMIC
+#define DSBUFL 100                     /* Disposition */
+    static char dsbuf[DSBUFL];
+#define SPBUFL 512                     /* System-dependent parameters */
+    static char spbuf[SPBUFL];
+#else
+#define DSBUFL 100                     /* Disposition */
+    static char *dsbuf = NULL;
+#define SPBUFL 512                     /* System-dependent parameters */
+    static char *spbuf = NULL;
+#endif /* DYNAMIC */
+#define RPBUFL 20                      /* Attribute reply */
+    static char rpbuf[RPBUFL];
+
+    char *rp;                          /* Pointer to reply buffer */
+    int retcode;                       /* Return code */
+
+/* fill in the attributes we have received */
+
+    rp = rpbuf;                                /* Initialize reply buffer */
+    *rp++ = 'N';                       /* for negative reply. */
+    retcode = 0;                       /* Initialize return code. */
+
+    while (c = *s++) {                 /* Get attribute tag */
+       aln = xunchar(*s++);            /* Length of attribute string */
+       switch (c) {
+         case '!':                     /* File length in K */
+           for (i = 0; (i < aln) && (i < ABUFL); i++) /* Copy it */
+             abuf[i] = *s++;
+           abuf[i] = '\0';             /* Terminate with null */
+           if (i < aln) s += (aln - i); /* If field was too long for buffer */
+           yy->lengthk = atol(abuf);   /* Convert to number */
+           break;
+
+         case '"':                     /* file type */
+           for (i = 0; (i < aln) && (i < FTBUFL); i++)
+             ftbuf[i] = *s++;          /* Copy it into a static string */
+           ftbuf[i] = '\0';
+           if (i < aln) s += (aln - i);
+           if (attypi) {               /* TYPE attribute is enabled? */
+               yy->type.val = ftbuf;   /* Pointer to string */
+               yy->type.len = i;       /* Length of string */
+               debug(F101,"gattr file type",tsbuf,i);
+               if (
+                   *ftbuf != 'A' && *ftbuf != 'B' /* Unknown type? */
+#ifdef VMS
+/* or (VMS) our FILE TYPE is LABELED and the incoming file is text... */
+                   || binary == XYFT_L && *ftbuf == 'A'
+#endif /* VMS */                   
+                   ) {
+                   discard = 1;        /* Reject the file */
+                   retcode = -1;
+                   *rp++ = c;
+                   break;
+               }
+/*
+  The following code moved here from opena() so we set binary mode
+  as soon as requested by the attribute packet.  That way when the first
+  data packet comes, the mode of transfer can be displayed correctly
+  before opena() is called.
+*/
+               if (bsavef) {           /* If somehow file mode */
+                   binary = bsave;     /* was saved but not restored, */
+                   bsavef = 0;         /* restore it. */
+                   debug(F101,"gattr restoring binary","",binary);
+               }
+               if (yy->type.val[0] == 'A') { /* Check received attributes. */
+                   bsave = binary;     /* ASCII.  Save global file type */
+                   bsavef = 1;         /* ( restore it in clsof() ) */
+                   binary = XYFT_T;    /* Set current type to Text. */
+                   debug(F100,"gattr attribute A = text","",binary); /*  */
+               } else if (yy->type.val[0] == 'B') {
+                   bsave = binary;     /* Binary.  Save global file type */
+                   bsavef = 1;
+#ifdef VMS
+                   if (binary != XYFT_L && binary != XYFT_U) /* VMS cases */
+#endif /* VMS */
+                     binary = XYFT_B;
+                   debug(F101,"gattr attribute B = binary","",binary);
+               }
+           }
+           break;
+
+         case '#':                     /* File creation date */
+           for (i = 0; (i < aln) && (i < DTBUFL); i++)
+             dtbuf[i] = *s++;          /* Copy it into a static string */
+           if (i < aln) s += (aln - i);
+           dtbuf[i] = '\0';
+           if (atdati) {
+               yy->date.val = dtbuf;   /* Pointer to string */
+               yy->date.len = i;       /* Length of string */
+               if (fncact == XYFX_U) { /* Receiving in update mode? */
+                   if (zstime(filnam,yy,1) > 0) { /* Compare dates */
+                       discard = 1;    /* If incoming file is older, */
+                       *rp++ = c;      /* discard it, reason = date. */
+                       retcode = -1;   /* Rejection notice. */
+                   }
+               }                               
+           }
+           break;
+
+         case '(':                     /* File Block Size */
+           for (i = 0; (i < aln) && (i < ABUFL); i++) /* Copy it */
+             abuf[i] = *s++;
+           abuf[i] = '\0';             /* Terminate with null */
+           if (i < aln) s += (aln - i);
+           if (atblki)
+             yy->blksize = atol(abuf); /* Convert to number */
+           break;
+
+         case '*':                     /* Encoding (transfer syntax) */
+           for (i = 0; (i < aln) && (i < TSBUFL); i++)
+             tsbuf[i] = *s++;          /* Copy it into a static string */
+           if (i < aln) s += (aln - i);
+           tsbuf[i] = '\0';
+           if (atenci) {
+               yy->encoding.val = tsbuf; /* Pointer to string */
+               yy->encoding.len = i;   /* Length of string */
+               debug(F101,"gattr encoding",tsbuf,i);
+               switch (*tsbuf) {
+#ifndef NOCSETS
+                 case 'A':             /* Normal (maybe extended) ASCII */
+                   tcharset = TC_USASCII; /* Transparent chars untranslated */
+                   break;
+                 case 'C':             /* Specified character set */
+                   for (i = 0; i < ntcsets; i++) { 
+                       if (!strcmp(tcsinfo[i].designator,tsbuf+1)) break;
+                   }
+                   debug(F101,"gattr xfer charset lookup","",i);
+                   if (i == ntcsets) { /* If unknown character set, */
+                       debug(F110,"gattr: xfer charset unknown",tsbuf+1,0);
+                       if (!unkcs) {   /* and SET UNKNOWN DISCARD, */
+                           retcode = -1; /* reject the file. */
+                           *rp++ = c;
+                       }
+                   } else {
+                       tcharset = tcsinfo[i].code; /* if known, use it */
+                       rx = xlr[tcharset][fcharset]; /* xlation function */
+                   }
+                   debug(F101,"gattr tcharset","",tcharset);
+               break;
+#endif /* NOCSETS */
+             default:                  /* Something else. */
+               debug(F110,"gattr unk encoding attribute",tsbuf,0);
+               if (!unkcs) {           /* If SET UNK DISC */
+                   retcode = -1;       /* reject the file */
+                   *rp++ = c;
+               }
+               break;
+               }
+           }
+           break;
+
+         case '+':                     /* disposition */
+#ifdef DYNAMIC
+           if (!dsbuf)
+               if ((dsbuf = malloc(DSBUFL+1)) == NULL)
+                   fatal("gtattr: no memory for dsbuf");
+#endif /* DYNAMIC */
+           for (i = 0; (i < aln) && (i < DSBUFL); i++)
+             dsbuf[i] = *s++;          /* Copy it into a static string */
+           dsbuf[i] = '\0';
+           if (i < aln) s += (aln - i);
+           if (atdisi) {
+               yy->disp.val = dsbuf;   /* Pointer to string */
+               yy->disp.len = i;       /* Length of string */
+               if (
+#ifndef datageneral                    /* MAIL supported only for */
+#ifndef OS2                            /* UNIX, VMS, and OS-9 */
+#ifndef MAC
+#ifndef GEMDOS
+#ifndef AMIGA
+                   *dsbuf != 'M' &&
+#endif /* AMIGA */
+#endif /* GEMDOS */
+#endif /* MAC */
+#endif /* OS/2 */
+#endif /* datageneral */
+                   *dsbuf != 'P') {
+                   retcode = -1;
+                   *rp++ = c;
+               }
+           }
+           break;
+
+         case '.':                     /* Sender's system ID */
+           for (i = 0; (i < aln) && (i < IDBUFL); i++)
+             idbuf[i] = *s++;          /* Copy it into a static string */
+           idbuf[i] = '\0';
+           if (i < aln) s += (aln - i);
+           if (atsidi) {
+               yy->systemid.val = idbuf; /* Pointer to string */
+               yy->systemid.len = i;   /* Length of string */
+           }
+           break;
+
+         case '0':                     /* System-dependent parameters */
+#ifdef DYNAMIC
+           if (!spbuf && !(spbuf = malloc(SPBUFL)))
+               fatal("gattr: no memory for spbuf");
+#endif /* DYNAMIC */
+           for (i = 0; (i < aln) && (i < SPBUFL); i++)
+             spbuf[i] = *s++;          /* Copy it into a static string */
+           spbuf[i] = '\0';
+           if (i < aln) s += (aln - i);
+           if (atsysi) {
+               yy->sysparam.val = spbuf; /* Pointer to string */
+               yy->sysparam.len = i;   /* Length of string */
+           }
+           break;
+
+         case '1':                     /* File length in bytes */
+           for (i = 0; (i < aln) && (i < ABUFL); i++) /* Copy it */
+             abuf[i] = *s++;
+           abuf[i] = '\0';             /* Terminate with null */
+           if (i < aln) s += (aln - i);
+           yy->length = atol(abuf);    /* Convert to number */
+           debug(F111,"gattr length",abuf,(int) yy->length);
+           break;
+
+         default:                      /* Unknown attribute */
+           s += aln;                   /* Just skip past it */
+           break;
+       }
+    }
+
+    /* Check file length now, because we also need to know the file type */
+    /* in case zchkspa() differentiates text and binary (VMS version does) */
+
+    if (atleni) {                      /* Length attribute enabled? */
+       if (yy->length > -1L) {         /* Length-in-bytes attribute rec'd? */
+           if (!zchkspa(filnam,(yy->length))) { /* Check space */
+               retcode = -1;
+               *rp++ = '1';
+           }
+       } else if (yy->lengthk > -1L) { /* Length in K attribute rec'd? */
+           if (!zchkspa(filnam,(yy->lengthk * 1024))) {
+               retcode = -1;           /* Check space */
+               *rp++ = '!';
+           }
+       }
+    }
+    if (yy->length > -1L) {            /* Get the file size */
+       fsize = yy->length;             
+    } else if (yy->lengthk > -1L) {
+       fsize = yy->lengthk * 1024L;
+    } else fsize = -1L;
+
+#ifdef DEBUG
+    if (deblog) {
+       sprintf(abuf,"%ld",fsize);
+       debug(F110,"gattr fsize",abuf,0);
+    }
+#endif /* DEBUG */
+    if (retcode == 0) rp = rpbuf;      /* Null reply string if accepted */
+    *rp = '\0';                                /* End of reply string */
+    yy->reply.val = rpbuf;             /* Add it to attribute structure */
+    yy->reply.len = (int)strlen(rpbuf);
+    debug(F111,"gattr return",rpbuf,retcode);
+    return(retcode);
+}
+
+/*  I N I T A T T R  --  Initialize file attribute structure  */
+
+int
+initattr(yy) struct zattr *yy; {
+    yy->lengthk = yy->length = -1L;
+    yy->type.val = "";
+    yy->type.len = 0;
+    yy->date.val = "";
+    yy->date.len = 0;
+    yy->encoding.val = "";
+    yy->encoding.len = 0;
+    yy->disp.val = "";
+    yy->disp.len = 0;
+    yy->systemid.val = "";
+    yy->systemid.len = 0;
+    yy->sysparam.val = "";
+    yy->sysparam.len = 0;
+    yy->creator.val = "";
+    yy->creator.len = 0;
+    yy->account.val = "";
+    yy->account.len = 0;
+    yy->area.val = "";
+    yy->area.len = 0;
+    yy->passwd.val = "";
+    yy->passwd.len = 0;
+    yy->blksize = -1L;
+    yy->access.val = "";
+    yy->access.len = 0;
+    yy->lprotect.val = "";
+    yy->lprotect.len = 0;
+    yy->gprotect.val = "";
+    yy->gprotect.len = 0;
+    yy->recfm.val = "";
+    yy->recfm.len = 0;
+    yy->reply.val = "";
+    yy->reply.len = 0;
+    return(0);
+}
+\f
+/*  A D E B U -- Write attribute packet info to debug log  */
+
+int
+adebu(f,zz) char *f; struct zattr *zz; {
+#ifdef DEBUG
+    if (deblog == 0) return(0);
+    debug(F110,"Attributes for incoming file ",f,0);
+    debug(F101," length in K","",(int) zz->lengthk);
+    debug(F111," file type",zz->type.val,zz->type.len);
+    debug(F111," creation date",zz->date.val,zz->date.len);
+    debug(F111," creator",zz->creator.val,zz->creator.len);
+    debug(F111," account",zz->account.val,zz->account.len);
+    debug(F111," area",zz->area.val,zz->area.len);
+    debug(F111," password",zz->passwd.val,zz->passwd.len);
+    debug(F101," blksize","",(int) zz->blksize);
+    debug(F111," access",zz->access.val,zz->access.len);
+    debug(F111," encoding",zz->encoding.val,zz->encoding.len);
+    debug(F111," disposition",zz->disp.val,zz->disp.len);
+    debug(F111," lprotection",zz->lprotect.val,zz->lprotect.len);
+    debug(F111," gprotection",zz->gprotect.val,zz->gprotect.len);
+    debug(F111," systemid",zz->systemid.val,zz->systemid.len);
+    debug(F111," recfm",zz->recfm.val,zz->recfm.len);
+    debug(F111," sysparam",zz->sysparam.val,zz->sysparam.len);
+    debug(F101," length","",(int) zz->length);
+    debug(F110," reply",zz->reply.val,0);
+#endif /* DEBUG */
+    return(0);
+}
+
+/*  O P E N A -- Open a file, with attributes.  */
+/*
+  This function tries to open a new file to put the arriving data in.  The
+  filename is the one in the srvcmd buffer.  File collision actions are:
+  OVERWRITE (the existing file is overwritten), RENAME (the new file is
+  renamed), BACKUP (the existing file is renamed), DISCARD (the new file is
+  refused), UPDATE (the incoming file replaces the existing file only if the
+  incoming file has a newer creation date).
+
+  Returns 0 on failure, nonzero on success.
+*/
+int
+opena(f,zz) char *f; struct zattr *zz; {
+    int x;
+    static struct filinfo fcb;         /* Must be static! */
+
+    debug(F111,"opena discard",f,discard);
+
+    adebu(f,zz);                       /* Write attributes to debug log */
+
+    ffc = 0L;                          /* Init file character counter */
+
+#ifdef COMMENT
+/*
+  This code moved to sattr().
+*/
+    if (bsavef) {                      /* If somehow file mode */
+       binary = bsave;                 /* was saved but not restored, */
+       bsavef = 0;                     /* restore it. */
+       debug(F101,"opena restoring binary","",binary);
+    }
+    if (zz->type.val[0] == 'A') {      /* Check received attributes */
+#ifdef VMS
+       if (binary == XYFT_L)           /* Refuse to receive a file in */
+         return(0);                    /* labeled mode if sent as text. */
+#endif /* VMS */
+       bsave = binary;                 /* ASCII.  Save global file type */
+       bsavef = 1;                     /* ( restore it in clsof() ) */
+       binary = XYFT_T;                /* Set current type to Text. */
+       debug(F100,"opena attribute A = text","",binary);
+    } else if (zz->type.val[0] == 'B') {
+       bsave = binary;                 /* Binary.  Save global file type */
+       bsavef = 1;
+#ifdef VMS
+       if (binary != XYFT_L && binary != XYFT_U) /* Special VMS cases */
+#endif /* VMS */
+         binary = XYFT_B;
+       debug(F101,"opena attribute B = binary","",binary);
+    }
+#endif /* COMMENT */
+
+    /* Set up file control structure */
+
+    fcb.bs = fblksiz;                  /* Blocksize */
+#ifndef NOCSETS
+    fcb.cs = fcharset;                 /* Character set */
+#else
+    fcb.cs = 0;                                /* Character set */
+#endif /* NOCSETS */
+    fcb.rl = frecl;                    /* Record Length */
+    fcb.fmt = frecfm;                  /* Record Format */
+    fcb.org = forg;                    /* Organization */
+    fcb.cc = fcctrl;                   /* Carriage control */
+    fcb.typ = binary;                  /* Type */
+    fcb.dsp = (fncact == XYFX_A) ? XYFZ_A : XYFZ_N; /* Disposition */
+    fcb.os_specific = '\0';            /* OS specific info */
+#ifdef VMS
+    fcb.lblopts = lf_opts;             /* VMS Labeled file options */
+#else
+    fcb.lblopts = 0;
+#endif /* VMS */
+
+    if (x = openo(f,zz,&fcb)) {                /* Try to open the file. */
+       tlog(F110," as",f,0L);          /* OK, open, record name. */
+       if (binary) {                   /* Log file mode in transaction log */
+           tlog(F101," mode: binary","",(long) binary);
+       } else {                        /* If text mode, check character set */
+           tlog(F100," mode: text","",0L);
+#ifndef NOCSETS
+           tlog(F110," file character set",fcsinfo[fcharset].name,0L);
+           if (tcharset == TC_TRANSP)
+             tlog(F110," xfer character set","transparent",0L);
+           else
+             tlog(F110," xfer character set",tcsinfo[tcharset].name,0L);
+#endif /* NOCSETS */
+           debug(F111," opena charset",zz->encoding.val,zz->encoding.len);
+       }
+       if (fsize > -1L) screen(SCR_FS,0,fsize,"");
+
+#ifdef datageneral
+/* Need to turn on multi-tasking console interrupt task here, since multiple */
+/* files may be received. */
+        if ((local) && (!quiet))        /* Only do this if local & not quiet */
+            consta_mt();                /* Start the asynch read task */
+#endif /* datageneral */
+
+    } else {                           /* Did not open file OK. */
+#ifdef ATTSV
+       extern int errno;
+       screen(SCR_EM,0,0l,sys_errlist[errno]);
+#else
+#ifdef BSD4
+       extern int errno;
+       screen(SCR_EM,0,0l,sys_errlist[errno]);
+#else
+       screen(SCR_EM,0,0l,"Can't open output file");
+#endif /* BSD4 */
+#endif /* ATTSV */
+
+        tlog(F110,"Failure to open",f,0L);
+
+
+    }
+    return(x);                         /* Pass on return code from openo */
+}
+\f
+/*  C A N N E D  --  Check if current file transfer cancelled */
+
+int
+canned(buf) CHAR *buf; {
+    if (*buf == 'X') cxseen = 1;
+    if (*buf == 'Z') czseen = 1;
+    debug(F101,"canned: cxseen","",cxseen);
+    debug(F101," czseen","",czseen);
+    return((czseen || cxseen) ? 1 : 0);
+}
+
+
+/*  O P E N I  --  Open an existing file for input  */
+
+int
+openi(name) char *name; {
+    int x, filno;
+    char *name2;
+
+    if (memstr) return(1);             /* Just return if file is memory. */
+
+    debug(F110,"openi",name,0);
+    debug(F101," sndsrc","",sndsrc);
+
+    filno = (sndsrc == 0) ? ZSTDIO : ZIFILE;    /* ... */
+
+    debug(F101," file number","",filno);
+
+    if (server && !en_cwd) {           /* If running as server */
+       zstrip(name,&name2);            /* and CWD is disabled... */
+       if (                            /* check if pathname was included. */
+#ifdef VMS
+       zchkpath(name)
+#else
+       strcmp(name,name2)
+#endif /* VMS */
+        ) {
+           tlog(F110,name,"authorization failure",0L);
+           debug(F110," openi authorization failure",name,0);
+           return(0);
+       } else name = name2;
+    }
+    if (zopeni(filno,name)) {          /* Otherwise, try to open it. */
+       debug(F110," ok",name,0);
+       return(1);
+    } else {                           /* If not found, */
+       char xname[100];                /* convert the name */
+       zrtol(name,xname);              /* to local form and then */
+       x = zopeni(filno,xname);        /* try opening it again. */
+       debug(F101," zopeni","",x);
+       if (x) {
+           debug(F110," ok",xname,0);
+           return(1);                  /* It worked. */
+        } else {
+#ifdef COMMENT
+           screen(SCR_EM,0,0l,"Can't open file");  /* It didn't work. */
+#endif /* COMMENT */
+           tlog(F110,xname,"could not be opened",0L);
+           debug(F110," openi failed",xname,0);
+           return(0);
+        }
+    }
+}
+\f
+/*  O P E N O  --  Open a new file for output.  */
+
+int
+openo(name,zz,fcb) char *name; struct zattr *zz; struct filinfo *fcb; {
+    char *name2;
+
+    if (stdouf)                                /* Receiving to stdout? */
+      return(zopeno(ZSTDIO,"",zz,NULL));
+
+    debug(F110,"openo: name",name,0);
+
+    if (cxseen || czseen || discard) { /* If interrupted, get out before */
+       debug(F100," open cancelled","",0); /* destroying existing file. */
+       return(1);                      /* Pretend to succeed. */
+    }
+    if (server && !en_cwd) {           /* If running as server */
+       zstrip(name,&name2);            /* and CWD is disabled, */
+       if (strcmp(name,name2)) {       /* check if pathname was included. */
+           tlog(F110,name,"authorization failure",0L);
+           debug(F110," openo authorization failure",name,0);
+           return(0);
+       } else name = name2;
+    }
+
+    if (zopeno(ZOFILE,name,zz,fcb) == 0) { /* Try to open the file */
+       debug(F110,"openo failed",name,0);
+       tlog(F110,"Failure to open",name,0L);
+       return(0);
+    } else {
+       debug(F110,"openo ok, name",name,0);
+       return(1);
+    }
+}
+
+/*  O P E N T  --  Open the terminal for output, in place of a file  */
+
+int
+opent(zz) struct zattr *zz; {
+    ffc = tfc = 0L;
+    if (bsavef) {                      /* If somehow file mode */
+       binary = bsave;                 /* was saved but not restored, */
+       bsavef = 0;                     /* restore it. */
+       debug(F101,"opena restoring binary","",binary);
+    }
+    bsave = binary;
+    bsavef = 1;
+    binary = XYFT_T;
+    return(zopeno(ZCTERM,"",zz,NULL));
+}
+\f
+/*  C L S I F  --  Close the current input file. */
+
+int
+clsif() {
+    int x = 0;
+#ifdef datageneral
+    if ((local) && (!quiet))    /* Only do this if local & not quiet */
+        if (nfils < 1)          /* More files to send ... leave it on! */
+            connoi_mt();
+#endif /* datageneral */
+    if (memstr) {                      /* If input was memory string, */
+       memstr = 0;                     /* indicate no more. */
+    } else x = zclose(ZIFILE);         /* else close input file. */
+    if (cxseen || czseen)              /* If interrupted */
+      screen(SCR_ST,ST_INT,0l,"");     /* say so */
+    else if (discard)                  /* If I'm refusing */
+      screen(SCR_ST,ST_REFU,0l,refused); /* say why */
+    else {                             /* Otherwise */
+       fstats();                       /* update statistics */
+       screen(SCR_ST,ST_OK,0l,"");     /* and say transfer was OK */
+    }
+    hcflg = 0;                         /* Reset flags */
+    *filnam = '\0';                    /* and current file name */
+    return(x);
+}
+
+
+/*  C L S O F  --  Close an output file.  */
+
+/*  Call with disp != 0 if file is to be discarded.  */
+/*  Returns -1 upon failure to close, 0 or greater on success. */
+
+int
+clsof(disp) int disp; {
+    int x;
+
+    debug(F101,"clsof disp","",disp);
+    if (bsavef) {                      /* If we saved global file type */
+       debug(F101,"clsof restoring binary","",binary);
+       binary = bsave;                 /* restore it */
+       bsavef = 0;                     /* only this once. */
+    }
+#ifdef datageneral
+    if ((local) && (!quiet))           /* Only do this if local & not quiet */
+        connoi_mt();
+#endif /* datageneral */
+    cxseen = 0;                                /* Reset per-file interruption flag */
+    if ((x = zclose(ZOFILE)) < 0) {    /* Try to close the file */
+       tlog(F100,"Failure to close",filnam,0L);
+       screen(SCR_ST,ST_ERR,0l,"");
+    } else if (disp) {                 /* Interrupted or refused */
+       if (keep == 0) {                /* If not keep incomplete files */
+           if (*filnam) zdelet(filnam); /* Delete it */
+           debug(F100,"Discarded","",0);
+           tlog(F100,"Discarded","",0L);
+           screen(SCR_ST,ST_DISC,0l,"");
+       } else {                        /* Keep incomplete copy */
+           if (ffc) ffc++;             /* This is off by one (why?) */
+           fstats();
+           debug(F100,"Incomplete","",0);
+           tlog(F100,"Incomplete","",0L);
+           screen(SCR_ST,ST_INC,0l,"");
+       }
+    } else {                           /* Nothing wrong, just keep it */
+       debug(F100,"Closed","",0);      /* and give comforting messages. */
+       if (ffc) ffc++;                 /* Correct off-by-1 error */
+       fstats();
+       screen(SCR_ST,ST_OK,0l,"");
+    }
+    return(x);                         /* Send back zclose() return code. */
+}
+
+#ifdef SUNOS4S5
+tolower(c) char c; { return((c)-'A'+'a'); }
+toupper(c) char c; { return((c)-'a'+'A'); }
+#endif /* SUNOS4S5 */
+