/*->c.ckcfn3 */

/*  C K C F N 3  --  Packet buffer management for C-Kermit  */

/* (plus assorted functions tacked on at the end) */






#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>






#include "h.def"





#include "ckcdeb.h"
#include "ckcker.h"
#include "ckcfil.h"

#include "ckcdef.h"






/* Pointers to translation functions */

#ifdef CKCXLA

extern CHAR (*xlr[MAXTCSETS+1][MAXFCSETS+1])(); 
extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])(); 
extern CHAR (*rx)();    /* Pointer to input character translation function */
extern CHAR (*sx)();    /* Pointer to output character translation function */

#endif











/* FUNCTIONS */

/* For sanity, use "i" for buffer slots, "n" for packet numbers. */


/* 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(int slots,int 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(int slots)
{
    int i, x;
    sbufnum = 0;
    if ((x = makebuf(slots,BIGSBUFSIZ,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. */
    }
    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(int slots)
{
    int i, x;
    rbufnum = 0;
    if ((x = makebuf(slots,BIGRBUFSIZ,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(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;
    if (deblog) dumpsbuf();
    if (deblog) dumprbuf();
    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(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 */
          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. */
          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(void)
{                             /* 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);
          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 "i"  */

/*  Returns:  */
/*   1 upon success  */
/*  -1 if specified buffer does not exist */

int freesbuf(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(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);
}












/*  C H K W I N  --  Check if packet n is in window. */

/*  Returns: */
/*    0 if it is in the current window,  */
/*   +1 if in 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(int n,int bottom,int 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);

    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(void)
{                            /* Dump send-buffers */

#ifdef DEBUG

    int j, x;
    char xbuf[200];


    if (! deblog) return(0);
    zsoutl(ZDFILE,"SEND BUFFERS:");
    zsoutl(ZDFILE,"buffer inuse address length data type seq flag retries");

    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
               );
        zsout(ZDFILE,xbuf);

        x = strlen(s_pkt[j].bf_adr);
        sprintf(xbuf,"[%.72s%s]\n",s_pkt[j].bf_adr, x > 72 ? "..." : "");
        zsout(ZDFILE,xbuf);
    }
    sprintf(xbuf,"free: %d, winlo: %d\n", sbufnum, winlo);
    zsout(ZDFILE,xbuf);

#endif

 return(0);
}




int dumprbuf(void)
{                            /* Dump receive-buffers */

#ifdef DEBUG

    int j, x;
    char xbuf[200];

    if (! deblog) return(0);
    zsoutl(ZDFILE,"RECEIVE BUFFERS:");
    zsoutl(ZDFILE,"buffer inuse address length data type seq flag retries");
    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
               );
        zsout(ZDFILE,xbuf);
        x = strlen(r_pkt[j].bf_adr);
        sprintf(xbuf,"[%.72s%s]\n",r_pkt[j].bf_adr, x > 72 ? "..." : "");
        zsout(ZDFILE,xbuf);
    }
    sprintf(xbuf,"free: %d, winlo: %d\n", rbufnum, winlo);
    zsout(ZDFILE,xbuf);

#endif

 return(0);
}







/*** 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(int xp)
{                     /* Send Attributes */
    int i, j, aln;
    char *tp;
    struct zattr x;

    if (zsattr(&x) < 0) return(-1);     /* Get attributes or die trying */
    nxtpkt();                           /* 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 (binary) {                   /* Binary  */
            data[i++] = tochar(2);      /*  Two characters */
            data[i++] = 'B';            /*  B for Binary */
            data[i++] = '8';            /*  8-bit bytes (note assumption...) */
        } 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 CKCXLA

            if (tcharset == TC_TRANSP)  /* Transfer character set */
              tp = NULL;
            else
              tp = tcsinfo[tcharset].designator;
            if ((tp != NULL) && (aln = 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 */
            }

#else
       tp=NULL;

#endif



        }
    }
    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(&data[i+2],"%ld",x.lengthk);
            aln = strlen(&data[i+2]);
            data[i+1] = tochar(aln);
            i += aln + 2;

            data[i] = '1';                      /* File length in bytes */
            sprintf(&data[i+2],"%d",fsize);
            aln = strlen(&data[i+2]);
            data[i+1] = tochar(aln);
            i += aln + 2;
        }
        if (atblko && fblksiz) {        /* Blocksize, if set */
            data[i] = '(';
            sprintf(&data[i+2],"%d",fblksiz);
            aln = strlen(&data[i+2]);
            data[i+1] = tochar(aln);
            i += aln + 2;
        }
    }
    if ((rprintf || rmailf) && atdiso) { /* MAIL, or REMOTE PRINT?  */
        data[i++] = '+';                /* Disposition */
        data[i++] = tochar(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];
    }
    data[i] = '\0';                     /* Make sure it's null-terminated */
    aln = strlen(data);                 /* Get overall length of attributes */
    if (aln > spsiz) {                  /* Check length of result */
        spack('A',pktnum,0,"");         /* send an empty attribute packet */
                                        /* DJP changed */

        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);
    }
    /* Change this code to break attribute data up into multiple packets! */
    return(0);
}







int rsattr(char * s)
{                    /* Read response to attribute packet */
    debug(F111,"rsattr: ",s,*s);        /* If it's 'N' followed by anything */
    if (*s == 'N') return(-1);          /* then other Kermit is refusing. */
    return(0);
}






int gattr(char * s,struct zattr * yy)
{ /* Read incoming attribute packet */
    char c;
    int aln, i;

#define ABUFL 100                       /* Temporary buffer for conversions */
    char abuf[ABUFL];
#define FTBUFL 10                       /* File type buffer */
    static char ftbuf[FTBUFL];
#define DTBUFL 24                       /* File creation date */
    static char dtbuf[DTBUFL];

#ifdef CKCXLA

#define TSBUFL 10                       /* Transfer syntax */
    static char tsbuf[TSBUFL];

#endif


#define IDBUFL 10                       /* System ID */
    static char idbuf[IDBUFL];
#define DSBUFL 100                      /* Disposition */
    static char dsbuf[DSBUFL];
#define SPBUFL 512                      /* System-dependent parameters */
    static char spbuf[SPBUFL];
#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++)!=NULL) {                  /* 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 (atleni) {
                yy->lengthk = atol(abuf);       /* Convert to number */


#ifdef NEVER
                if(!zchkspa(filnam,((long) (yy->lengthk * 1024) )))
                {
                  retcode = -1;       /* Check space */
                 *rp++ = c;
                }
#endif


            }
            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 (attypi) {
                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') { /* Reject unknown type */
                    retcode = -1;
                    *rp++ = c;
                }               
            }
            break;

          case '#':                     /* file creation date */
            for (i = 0; (i < aln) && (i < DTBUFL); i++)
              dtbuf[i] = *s++;          /* Copy it into a static string */
            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 (atblki) {
                yy->blksize = atol(abuf); /* Convert to number */
            }
            break;


#ifdef CKCXLA

          case '*':                     /* encoding (transfer syntax) */
            for (i = 0; (i < aln) && (i < TSBUFL); i++)
              tsbuf[i] = *s++;          /* Copy it into a static string */
            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) {
                  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;
              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;


#endif /* CKCXLA */




          case '+':                     /* disposition */
            for (i = 0; (i < aln) && (i < DSBUFL); i++)
              dsbuf[i] = *s++;          /* Copy it into a static string */
            dsbuf[i] = '\0';
            if (atdisi) {
                yy->disp.val = dsbuf;   /* Pointer to string */
                yy->disp.len = i;       /* Length of string */
                if (*dsbuf != 'M' && *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 (atsidi) {
                yy->systemid.val = idbuf; /* Pointer to string */
                yy->systemid.len = i;   /* Length of string */
            }
            break;

          case '0':                     /* system-dependent parameters */
            for (i = 0; (i < aln) && (i < SPBUFL); i++)
              spbuf[i] = *s++;          /* Copy it into a static string */
            spbuf[i] = '\0';
            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 (atleni) {
                yy->length = atol(abuf); /* Convert to number */
                debug(F101,"gattr length","",(int) yy->length);


#ifdef NEVER
                if(!zchkspa(filnam,( yy->length)))
                { /* Check space */
                 retcode = -1;
                 *rp++ = c;
                }
#endif

            }
            break;

          default:                      /* Unknown attribute */
            s += aln;                   /* Just skip past it */
            break;
        }
    }

                                        /* (PWP) show the info */
 if(yy->length>0)
 {                                      /* Let the user know the size */
  fsize=(int) yy->length;             
/*  screen(SCR_QE,0,fsize," Size");  */
 }
 else
 if(yy->lengthk>0)
 {
  fsize=((int)yy->lengthk)*1024;
/*  screen(SCR_QE,0,fsize," Size");  */
 }


 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=strlen(rpbuf);
 debug(F111,"gattr",rpbuf,retcode);
 return(retcode);
}







/*  I N I T A T T R  --  Initialize file attribute structure  */

int initattr(struct zattr * yy)
{
    yy->lengthk = -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);
}








/*  A D E B U -- Write attribute packet info to debug log  */

int adebu(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,0);
    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);

#else

    f=NULL;
    zz=NULL;


#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.  If warning is on and
  a file of that name already exists, then a unique name is chosen.
*/



int opena(char * f,struct zattr * zz)
{
    int x;
    static struct filinfo fcb;          /* Must be static! */

    adebu(f,zz);                        /* Write attributes to debug log */

    ffc = 0;                            /* Init file character counter */

    if (bsavef) {                       /* If somehow file mode */
        binary = bsave;                 /* was saved but not restored, */
        bsavef = 0;                     /* restore it. */
        debug(F101,"opena restoring binary","",binary);
    }

    /* Set up file control structure */

    fcb.bs = fblksiz;                   /* Blocksize */

#ifdef CKCXLA
    fcb.cs = fcharset;                  /* Character set */
#else
    fcb.cs=0;
#endif

    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 */

    if((x=openo(f,zz,&fcb))!=NULL) {         /* Try to open the file. */
        tlog(F110," as",f,0l);          /* OK, open, record name. */
        if (zz->type.val[0] == 'A') {   /* Check attributes */
            bsave = binary;             /* Save global file type */
            bsavef = 1;                 /* ( restore it in clsof() ) */
            binary = 0;
            debug(F100,"opena attribute A = text","",binary);
        } else if (zz->type.val[0] == 'B') {
            bsave = binary;             /* Save global file type */
            bsavef = 1;
            binary = 1;
            debug(F100,"opena attribute B = binary","",binary);
        }
        if (binary) {                   /* Log file mode in transaction log */
            tlog(F100," mode: binary","",0l);
        } else {                        /* If text mode, check character set */
            tlog(F100," mode: text","",0l);
            debug(F111," opena charset",zz->encoding.val,zz->encoding.len);
        }

#ifdef NEVER
        screen(SCR_AN,0,0l,f);
        intmsg(filcnt);
#endif



    } else {                            /* Did not open file OK. */
        tlog(F110,"Failure to open",f,0l);
        screen(SCR_EM,0,0l,transtoken("KS0")); /* Can't open file */
    }
    return(x);                          /* Pass on return code from openo */
}







/*  C A N N E D  --  Check if current file transfer cancelled */

int canned(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(char * name)
{
    int x, filno;
    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 (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 {                                 /*Can't open file */
            screen(SCR_EM,0,0l,transtoken("KS0"));  /* It didn't work. */
            tlog(F110,xname,"could not be opened",0l);
            debug(F110," openi failed",xname,0);
            return(0);
        }
    }
}





/*  O P E N O  --  Open a new file for output.  */

int openo(char * name,struct zattr * zz,struct filinfo * fcb)
{
 debug(F110,"openo: name",name,0);

 if(cxseen || czseen)
 {             /* If interrupted, get out before */
  debug(F100," open cancelled","",0); /* destroying existing file. */
  return(1);                      /* Pretend to succeed. */
 }

 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(struct zattr * zz)
{
 ffc = tfc = 0;
 return(zopeno(ZCTERM,"",zz,NULL));
}








/*  C L S I F  --  Close the current input file. */

void clsif(int errflg)
{                                           /* If input was memory string, */
 if(memstr) memstr=0;                       /* indicate no more. */
 else       zclose(ZIFILE,errflg);          /* else close input file. */

 if(czseen || cxseen) 
        screen(SCR_ST,ST_DISC,0l,"");
 else
        screen(SCR_ST,ST_OK,0l,"");

 hcflg = 0;                                 /* Reset flags, */
 *filnam = '\0';                            /* and current file name */
}





/*  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(int disp)
{
 int x;

 if(bsavef)
 {                       /* If we saved global file type */
  debug(F101,"clsof restoring binary","",binary);
  binary=bsave;                 /* restore it */
  bsavef=0;                     /* only this once. */
 }

 if((x=zclose(ZOFILE,disp))<0)      /* Try to close the file */
 {  
  tlog(F100,"Failure to close",filnam,0l);
  screen(SCR_ST,ST_ERR,0l,transtoken("KS1")); /*closing file.*/
 }
 else
 if(disp && (keep==0))
 {   /* Delete it if interrupted, */
  if(*filnam) zdelet(filnam);    /* and not keeping incomplete files */
  debug(F100,"Discarded","",0);
  tlog(F100,"Discarded","",0l);
  screen(SCR_ST,ST_DISC,0l,"");
 }
 else
 {                            /* Nothing wrong, just keep it */
  debug(F100,"Closed","",0);      /* and give comforting messages. */
  screen(SCR_ST,ST_OK,0l,"");
 }
 return(x);                          /* Send back zclose() return code. */
}


