/*
    FNS_C - QL-Kermit protocol functions

    Based on ckcfns.c, (C) Columbia University
*/


/* Include files */

#include "ram1_ker_h"                            /* Kermit definitions */


/* External variables */

extern int slen;                                 /* Maximum send packet size */
extern int rlen;                                 /* Maximum receive packet size */
extern int tslen;                                /* Send packet size in use */
extern int stmo;                                 /* Timeout remote is to use */
extern int rtmo;                                 /* Timeout we will use */
extern int n;                                    /* Packet number */
extern int ttyfd;                                /* FD of TTY for I/O, 0 if remote */
extern int ttychid;                              /* Channel ID for ttyfd */
extern int tvalue;                               /* Current timeout value */
extern int debug;                                /* Level of debugging output */
extern int parity;                               /* Current parity setting */
extern int npad;                                 /* Amount of padding remote needs */
extern int tnpad;                                /* Padding count in use */
extern int xfertyp;                              /* File transfer mode */

extern long filein;                              /* File bytes received */
extern long fileout;                             /* File bytes sent */
extern long totin;                               /* Total bytes received */
extern long totout;                              /* Total bytes sent */

extern bool timer;                               /* Timer enabled */
extern bool cxseen;                              /* File interruption flag */
extern bool czseen;                              /* Group interruption flag */
extern bool toscr;                               /* Screen data flag */
extern bool ebqflg;                              /* 8-bit quoting being done */

extern char ssop;                                /* SOP remote needs */
extern char rsop;                                /* SOP I need */
extern char seol;                                /* EOL for sending packets */
extern char reol;                                /* EOL for receiving packets */
extern char tseol;                               /* Sending EOL in use */
extern char quote;                               /* Sending control quote */
extern char pebq;                                /* 8-bit quote prefix */
extern char tquote;                              /* Receiving control quote */
extern char ebq;                                 /* 8-bit quote in use */

extern unsigned char padch;                      /* Pad character remote needs */
extern unsigned char tpadc;                      /* Pad character in use */

extern char *ttyname;                            /* Name of TTY for transfer */

extern int fp;                                   /* Local file for data transfer */

extern int _oserr;                               /* QDOS error code */
extern int oserr;                                /* Copy of above */

extern bool warn8;                               /* Lost 8th bit warning given */

extern int state;                                /* Current switcher state */


/* External functions */

extern bool chkquote();                          /* Check for valid quote character */
extern bool tfput();                             /* Put a byte to transfer file */
extern int tfget();                              /* Get a byte from transfer file */


/* Local variables */

static int length;                               /* Length of last packet read */


/* TINIT - Initialise transaction */

tinit()
{
    tseol = seol;                                /* Initialise sending EOL, */
    tslen = slen;                                /* packet length */
    tvalue = 50*rtmo;                            /* and timeout */
    czseen = FALSE;                              /* Clear interrupt flags */
    cxseen = FALSE;
    tnpad = npad;                                /* Initialise padding */
    tpadc = padch;
    tquote = quote;                              /* and quote characters */
    ebq = 0;
    ebqflg = FALSE;                              /* Reset 8-bit quote flag */
    totin = totout = 0;                          /* Clear character counts */
}


/* SPACK - Send a packet */

spack(type,num,len,data)
char type;
int num,len;
char *data;
{
    int i;                                       /* Character loop counter */
    char chksum;                                 /* Checksum */
    char buffer[110];                            /* Packet buffer */
    char *bufp;                                  /* Buffer pointer */

    if (debfull)                                 /* Display outgoing packet? */
    {
         if (data!=NULL) data[len] = '\0';       /* Null-terminate data for printing */
         printf("spack: type=%c, num=%d, len=%d\n",type,num,len);
         if (data!=NULL) printf(" data=\"%s\"\n",data);
    }

    bufp = buffer;                               /* Set up buffer pointer */

    *bufp++ = ssop;                              /* Packet marker (SOH) */

    chksum = *bufp++ = tochar(len+3);            /* Character count */

    *bufp++ = tochar(num);                       /* Packet number */
    chksum += tochar(num);                       /* Update checksum */

    *bufp++ = type;                              /* Packet type */
    chksum += type;                              /* Update checksum */

    for (i=0; i<len; i++)                        /* Loop for all data characters */
    {
         *bufp++ = data[i];                      /* Put a character */
         chksum += data[i];                      /* Update checksum */
    }

    chksum = (((chksum&0300)>>6)+chksum)&077;    /* Compute final checksum */
    *bufp++ = tochar(chksum);                    /* Put it in the packet */
    *bufp = tseol;                               /* Add packet line terminator */
    write(ttyfd,buffer,bufp-buffer+1);           /* and send the packet */

    for (i = 0; i<tnpad; i++)
         write(ttyfd,&tpadc,1);                  /* Put out any padding needed */
}


/* RPACK - Read a packet */

int rpack(len,num,data)
int *len,*num;
char *data;
{
    int i;                                       /* Loop counter */
    bool done;                                   /* Loop exit */
    char type;                                   /* Packet type */
    int t;                                       /* Current input character */
    char ccsum;                                  /* Our (computed) checksum */
    char rcsum;                                  /* Checksum received from host */

    done = FALSE;                                /* Start to look for a packet */
    do
    {
         if ((t = tread())<0) return(t);         /* Error */
    } while (t!=rsop);                           /* Look for start of packet */

    while (!done)                                /* Got SOP, loop to get packet */
    {
         if ((t = tread())<0) return(t);         /* Error */
         if (t==rsop) continue;                  /* Aborted packet */
         ccsum = t;                              /* Start the checksum */
         *len = unchar(t)-3;                     /* Character count */

         if ((t = tread())<0) return(t);         /* Error */
         if (t==rsop) continue;                  /* Aborted packet */
         ccsum += t;                             /* Update checksum */
         *num = unchar(t);                       /* Packet number */

         if ((t = tread())<0) return(t);         /* Error */
         if (t==rsop) continue;                  /* Aborted packet */
         ccsum += t;                             /* Update checksum */
         type = t;                               /* Packet type */

         for (i=0; i<*len; i++)                  /* Loop for data */
         {
              if ((t = tread())<0) return(t);    /* Error */
              if (t==rsop) goto xx;              /* Aborted packet */
              ccsum += t;                        /* Update checksum */
              data[i] = t;                       /* Put it in the data buffer */
         }
         data[*len] = '\0';                      /* Mark the end of the data */

         if ((t = tread())<0) return(t);         /* Error */
xx:      if (t==rsop) continue;                  /* Aborted packet */
         rcsum = unchar(t);                      /* Convert checksum to numeric */

         done = TRUE;                            /* Got checksum, done */
         length = *len;                          /* Save length for rpar() */
    }

    if (debfull)                                 /* Display incoming packet */
    {
         if (data!=NULL) data[*len] = '\0';      /* Null-terminate data for printing */
         printf("rpack type: %c, num: %d, len: %d\n",type,*num,*len);
         if (data!=NULL) printf(" data: \"%s\"\n",data);
    }

                                                 /* Fold in bits 7,8 to */
    ccsum = (((ccsum&0300)>>6)+ccsum)&077;       /* compute final checksum */

    if (ccsum!=rcsum)                            /* Checksum correct? */
    {
         if (debon) printf("Received bad checksum\n");
         return(BAD);
    }
    return((int) type);                          /* All OK, return packet type */
}


/* BUFILL - Get a bufferful of data from the file that's being sent and
   encode it for transmission.
*/

int bufill(buff)
char buff[];
{
    int i;                                       /* Loop index */
    char t;                                      /* Character read from file */
    int tt;                                      /* For testing EOF */
    int a7,b8;                                   /* Split-up versions of above */

    i = 0;                                       /* Set up data buffer pointer */
    while ((tt = tfget())!=EOF)                  /* Get the next character */
    {
         fileout++;                              /* Count it */

         t = (char) tt;
         a7 = t&0177;                            /* Get lower 7 bits */
         b8 = t&0200;                            /* and 8th bit */

         if (a7!=t && !warn8 && !ebqflg && parity!=PYNONE)
         {                                       /* Check for data loss */
              printf("Warning: 8th bits lost\n");
              warn8 = TRUE;
         }

         if (ebqflg && b8!=0)                    /* EBQ required? */
         {
              buff[i++] = ebq;                   /* Yes, put out prefix */
              t = a7;                            /* and work on 7-bit character */
         }

         if (a7<SP || a7==DEL)                   /* Control quote required? */
         {
              buff[i++] = quote;                 /* Yes, output prefix */
              if (t==LF && xfertyp==FTASC)       /* EOL in ASCII mode */
              {
                   buff[i++] = 'M';              /* Standard Kermit EOL sequence */
                   buff[i++] = quote;
                   t = 'J';
              }
              else t = ctl(t);                   /* Uncontrollify */
         }

         if (a7==quote || (a7==ebq && ebqflg))
         {
              buff[i++] = quote;                 /* Quote control prefix or EBQ */
         }

         buff[i++] = t;                          /* Finally output the character */

         if (i>=tslen-9) return(i);              /* Conservative length check */
    }
    if (i==0) return(EOF);                       /* Here only on EOF */
    return(i);                                   /* Handle partial buffer */
}


/* BUFEMP - Put data from an incoming packet into a file */

bool bufemp(buff,len)
char buff[];
int len;
{
    int i;                                       /* Counter */
    char t;                                      /* Character holder */
    int a,a7,b8;                                 /* Component parts of above */
    char fbuf[MAXPACK];                          /* File data buffer */
    int fbufp = 0;                               /* Index into above */

    for (i=0; i<len; i++)                        /* Loop through the data */
    {
         a = buff[i];                            /* Get character */
         b8 = 0;                                 /* Clear high bit flag */

         if (ebqflg && a==ebq)                   /* An 8-bit prefixed sequence? */
         {
              b8 = 0200;                         /* Set 8th bit for later */
              a = buff[++i];                     /* and get prefixed character */
         }

         if (a==tquote)                          /* Control quote? */
         {
              a = buff[++i];                     /* Yes, get what it prefixes */
              a7 = a&0177;                       /* Split off lower 7 bits */
              if ((a7>=0100 && a7<=0137) || a7=='?')
                   a = ctl(a);                   /* Uncontrollify */
         }

         t = (char) a | b8;                      /* Add in 8th bit */

         if (toscr && t!=CR) putch(t);           /* Screen output */
         else if (!(t==CR && xfertyp==FTASC))    /* File output */
              fbuf[fbufp++] = t;
         filein++;                               /* Count the character */
    }

    if (!toscr)                                  /* File output */
    {
         if (write(fp,fbuf,fbufp)!=fbufp)        /* Write to file */
         {
              oserr = _oserr;                    /* Save error code */
              return(FALSE);
         }
    }
    else if (n==1) putch('\n');                  /* Set screen for DO results */
    return(TRUE);
}


/* SPAR - Fill the data array with my Send-Init parameters */

int spar(data)
char data[];
{
    data[0] = tochar(rlen);                      /* Biggest packet I can receive */
    data[1] = tochar(stmo);                      /* When I want to be timed out */
    data[2] = tochar(0);                         /* How much padding I need */
    data[3] = ctl('\0');                         /* Padding character I need */
    data[4] = tochar(reol);                      /* End-Of-Line character I need */
    data[5] = quote;                             /* Quote character I send */

    if (parity!=PYNONE || ebqflg)                /* 8-bit quoting required? */
    {
         data[6] = pebq;                         /* Ask for what we have SET */
         if (chkquote(ebq) || ebq=='Y')
              ebqflg = TRUE;                     /* If valid reply, set flag */
    }
    else data[6] = 'Y';                          /* Not required, just say we're willing */

    data[7] = '1';                               /* Checksum type 1 */
    data[8] = ' ';                               /* No repeat count capability */

    return(9);                                   /* Return the length */
}


/* RPAR - Get and analyse the remote's Send-Init parameters */

#define IFP        if (length-->0)               /* If field is present */

rpar(data)
char data[];
{
    IFP tslen = min(unchar(data[0]),tslen);      /* Maximum send packet size */
    IFP tvalue = max(50*unchar(data[1]),tvalue); /* When I should time out */
    IFP tnpad = max(unchar(data[2]),tnpad);      /* Padding required */
    IFP tpadc = ctl(data[3]);                    /* Padding character */
    IFP tseol = unchar(data[4]);                 /* EOL character I must send */
    IFP tquote = data[5];                        /* Incoming data quote character */

    IFP
    {
         ebq = data[6];                          /* EBQ remote will use */

         if (chkquote(ebq)) ebqflg = TRUE;       /* If acceptable, we will too */
         else if ((parity!=PYNONE || ebqflg) && ebq=='Y')
         {                                       /* We need it, remote says OK */
              ebqflg = TRUE;
              ebq = pebq;                        /* Use what we have SET */
         }
         else if (ebq=='N')
         {
              ebqflg = FALSE;                    /* We need it, remote can't do */
         }
         else ebqflg = FALSE;                    /* Some other reply */
    }
    else ebqflg = FALSE;                         /* No reply */

    if (tseol==0) tseol = '\r';                  /* Check and set defaults */
    if (tquote==0) tquote = quote;
    if (tslen<10) tslen = 10;

    if (debon)
    {
         printf("rpar:  timeout=%d, length=%d, EOL=%d\n",tvalue/50,tslen,tseol);
         printf("       npad=%d, padchar=0%o, quote=%c\n",tnpad,tpadc,tquote);
         printf("       8-bit quoting %s",(ebqflg ? "yes" : "no"));
         if (ebqflg) printf(", with %c",ebq);
         printf("\n");
    }
}


/* ACK - Send an acknowledgement with no data */

ack()
{
     spack('Y',n,0,0);
}


/* ACK1 - Send an acknowledgement with 1 character of data */

ack1(s)
char *s;
{
     spack('Y',n,1,s);
}


/* ACKN - Acknowledge a specific packet number */

ackn(num)
int num;
{
     spack('Y',num,0,0);
}


/* NAK - Send a negative acknowledegement */

nak()
{
     spack('N',n,0,0);
}
