/* Embedded Kermit protocol module  */
/* MRU: Fri Mar 15 19:13:22 2002    */

/*
  Author: Frank da Cruz.
  Copyright (C) 1995, 2002, 
  Trustees of Columbia University in the City of New York.
  All rights reserved.
*/

/*
  No stdio or other runtime library calls, no system calls, no system 
  includes, no static data, and no global variables in this module.
*/
#include "vxWorks.h"
#include "stdlib.h"
#include "string.h"
#include "stdio.h"
#include "kermit.h"

#ifdef DEBUG                            /* ... except if debugging ... */
extern int debug, errorrate;
extern FILE * dp;
#endif /* DEBUG */

#define zgetc() ((--(k->zincnt))>=0)?((int)(*(k->zinptr)++)&0xff):readfile(k)

int					/* The kermit() function */
kermit(short f,				/* Function code */
       struct k_data *k,		/* The control struct */
       short r_slot,			/* Received packet slot number */
       int len,				/* Length of packet in slot */
       char *msg,			/* Message for error packet */
       struct k_response *r) {		/* Response struct */

    int i, j, rc;			/* Workers */
    int datalen;                        /* Length of packet data field */
    int bctu;				/* Block check type for this packet */
    UCHAR *p;                           /* Pointer to packet data field */
    UCHAR *q;                           /* Pointer to data to be checked */
    UCHAR c, t;                         /* Workers */
    UCHAR pbc[4];                       /* Copy of packet block check */
    short seq, prev;			/* Copies of sequence numbers */
    short chklen;                       /* Length of packet block check */
    unsigned int crc;                   /* 16-bit CRC */
    int ok;

#ifdef DEBUG
    if (debug) {
	fprintf(dp,"-------------\n");
	fprintf(dp,"f=%d state=%d\n",f,k->state);
    }
#endif /* DEBUG */

    if (f == K_INIT) {			/* Initialize packet buffers etc */

        r->filename[0] = '\0';		/* No filename yet. */
        r->filedate[0] = '\0';		/* No filedate yet. */
        r->filesize = 0L;               /* No filesize yet. */

        for (i = 0; i < P_WSLOTS; i++) { /* Packet info for each window slot */
	    freerslot(k,i);
	    freesslot(k,i);
	}
#ifdef F_TSW
        for (i = 0; i < 64; i++) {	/* Packet finder array */
	    k->r_pw[i] = -1;		/* initialized to "no packets yet" */
	    k->s_pw[i] = -1;		/* initialized to "no packets yet" */
	}
#endif /* F_TSW */

/* Initialize the k_data structure */    

	for (i = 0; i < 6; i++)
	  k->s_remain[i] = '\0';

        k->state    = R_WAIT;		/* Beginning protocol state */
	k->what     = W_RECV;		/* Default action */
	k->s_first  = 1;		/* Beginning of file */
        k->r_soh    = k->s_soh = SOH;	/* Packet start */
        k->r_eom    = k->s_eom = CR;	/* Packet end */
        k->s_seq    = k->r_seq =  0;	/* Packet sequence number */
        k->s_type   = k->r_type = 0;	/* Packet type */
        k->r_timo   = P_R_TIMO;		/* Timeout interval for me to use */
        k->s_timo   = P_S_TIMO;		/* Timeout for other Kermit to use */
        k->r_maxlen = P_PKTLEN;         /* Maximum packet length */
        k->s_maxlen = P_PKTLEN;         /* Maximum packet length */
        k->window   = P_WSLOTS;		/* Maximum window slots */
        k->wslots   = 1;		/* Current window slots */

        /* Parity must be filled in by the caller */

        k->retry  = P_RETRY;            /* Retransmission limit */
        k->s_ctlq = k->r_ctlq = '#';    /* Control prefix */
        k->ebq    = 'Y';		/* 8th-bit prefix negotiation */
	k->ebqflg = 0;			/* 8th-bit prefixing flag */
        k->rptq   = '~';		/* Send repeat prefix */
        k->rptflg = 0;                  /* Repeat counts negotiated */
	k->s_rpt  = 0;			/* Current repeat count */
        k->capas  = 0                   /* Capabilities */
#ifdef F_LP
          | CAP_LP                      /* Long packets */
#endif /* F_LP */
#ifdef F_SW
            | CAP_SW                    /* Sliding windows */
#endif /* F_SW */
#ifdef F_AT
              | CAP_AT                  /* Attribute packets */
#endif /* F_AT */
                ;

	k->opktbuf[0] = '\0';		/* No packets sent yet. */
	k->opktlen = 0;

/* This is the only way to initialize these tables -- no static data. */

        k->crcta[ 0] =       0L;        /* CRC generation table A */
        k->crcta[ 1] =  010201L;
        k->crcta[ 2] =  020402L;
        k->crcta[ 3] =  030603L;
        k->crcta[ 4] =  041004L,
        k->crcta[ 5] =  051205L;
        k->crcta[ 6] =  061406L;
        k->crcta[ 7] =  071607L;
        k->crcta[ 8] = 0102010L;
        k->crcta[ 9] = 0112211L;
        k->crcta[10] = 0122412L;
        k->crcta[11] = 0132613L;
        k->crcta[12] = 0143014L,
        k->crcta[13] = 0153215L;
        k->crcta[14] = 0163416L;
        k->crcta[15] = 0173617L;

        k->crctb[ 0] =       0L;        /* CRC table B */
        k->crctb[ 1] =  010611L;
        k->crctb[ 2] =  021422L;
        k->crctb[ 3] =  031233L;
        k->crctb[ 4] =  043044L,
        k->crctb[ 5] =  053655L;
        k->crctb[ 6] =  062466L;
        k->crctb[ 7] =  072277L;
        k->crctb[ 8] = 0106110L;
        k->crctb[ 9] = 0116701L;
        k->crctb[10] = 0127532L;
        k->crctb[11] = 0137323L;
        k->crctb[12] = 0145154L,
        k->crctb[13] = 0155745L;
        k->crctb[14] = 0164576L;
        k->crctb[15] = 0174367L;
	return(X_OK);

    } else if (f == K_SEND) {
	if (rpar(k,'S') != X_OK)	/* Send S packet with my parameters */
	  return(X_ERROR);		/* I/O error, quit. */
	k->state = S_INIT;		/* All OK, switch states */
	k->what = W_SEND;		/* Act like a sender */
        return(X_OK);

    } else if (f == K_STATUS) {         /* Status report requested. */
        r->status = k->state;           /* Say what state I'm in. */
        return(X_STATUS);               /* File name, date, size, if any. */

    } else if (f == K_QUIT) {           /* You told me to quit */
        return(X_DONE);                 /* so I quit. */

    } else if (f == K_ERROR) {          /* Send an error packet... */
        epkt(msg,k);
        return(X_DONE);                 /* and quit. */

    } else if (f != K_RUN) {            /* Anything else is an error. */
        return(X_ERROR);
    }
    if (k->state == R_NONE)             /* (probably unnecessary) */
      return(X_OK);

/* If we're in the protocol, check to make sure we got a new packet */

#ifdef DEBUG
    if (debug) fprintf(dp,"r_slot=%d length=%d\n",r_slot,len);
#endif /* DEBUG */

    if (r_slot < 0)			/* We should have a slot here */
      return(K_ERROR);
    else
      k->ipktinfo[r_slot].len = len;	/* Copy packet length to ipktinfo. */
    
    if (len < 4) {			/* Packet obviously no good? */
	if (k->what == W_RECV)		/* If receiving */
	  return(nak(k,k->r_seq,r_slot)); /* Send NAK for the packet we want */
	else				/* If sending */
	  return(resend(k));		/* retransmit last packet. */
    }

/* Parse the packet */    

    p = &(k->ipktbuf[0][r_slot]);	/* Point to it */

    q = p;                              /* Pointer to data to be checked */
    k->ipktinfo[r_slot].len = xunchar(*p++); /* Length field */
    seq = k->ipktinfo[r_slot].seq = xunchar(*p++); /* Sequence number */
    t = k->ipktinfo[r_slot].typ = *p++;	/* Type */

    if ((k->what == W_RECV) &&		/* Echo (it happens), ignore */
	(t == 'N' || t  == 'Y')) {
        freerslot(k,r_slot);
        return(X_OK);
    }
    k->ipktinfo[r_slot].dat = p;	/* Data field, maybe */
    if (k->ipktinfo[r_slot].len == 0) {	/* Length 0 means long packet */
        c = p[2];                       /* Get header checksum */
        p[2] = '\0';
        if (xunchar(c) != chk1(p-3)) {  /* Check it */
            freerslot(k,r_slot);	/* Bad */
#ifdef DEBUG
            if (debug) fprintf(dp,"HDR CHKSUM BAD\n");
#endif /* DEBUG */
	    if (k->what == W_RECV)
	      return(nak(k,k->r_seq,r_slot)); /* Send NAK */
	    else
	      return(resend(k));
        }
#ifdef DEBUG
	if (debug) fprintf(dp,"HDR CHKSUM OK\n"); /* Good */
#endif /* DEBUG */
        p[2] = c;                       /* Put checksum back */
        datalen = xunchar(p[0])*95 + xunchar(p[1]) - k->bct; /* Data length */
        p += 3;                         /* Fix data pointer */
        k->ipktinfo[r_slot].dat = p;	/* Permanent record of data pointer */
    } else {                            /* Regular packet */
        datalen = k->ipktinfo[r_slot].len - k->bct - 2; /* Data length */
    }
    if (t == 'S' || k->state == S_INIT) { /* S-packet was retransmitted? */
        chklen = 1;                     /* Block check is always type 1 */
        datalen = k->ipktinfo[r_slot].len - 3; /* Data length */
    } else chklen = k->bct;

#ifdef DEBUG
    if (debug)
      fprintf(dp,"bct=%d datalen=%d chklen=%d\n",k->bct,datalen,chklen);
#endif /* DEBUG */

    for (i = 0; i < chklen; i++)        /* Copy the block check */
      pbc[i] = p[datalen+i];
    pbc[i] = '\0';			/* Null-terminate block check string */
    p[datalen] = '\0';			/* and the packet DATA field. */
    switch (chklen) {                   /* Check the block check  */
      case 1:				/* Type 1, 6-bit checksum */
	ok = (xunchar(*pbc) == chk1(q));
#ifdef DEBUG
	if (ok && xerror()) ok = 0;
#endif /* DEBUG */
	if (!ok) {
	    freerslot(k,r_slot);
	    if (k->what == W_RECV)
	      nak(k,k->r_seq,r_slot);
	    else
	      resend(k);
	    return(X_OK);
	}
	break;

      case 2:                         /* Type 2, 12-bit checksum */
	i = xunchar(*pbc) << 6 | xunchar(pbc[1]);
	ok = (i == chk2(q));
#ifdef DEBUG
	if (ok && xerror()) ok = 0;
#endif /* DEBUG */
	if (!ok) {			/* No match */
	    if (t == 'E') {		/* Allow E packets to have type 1 */
		int j;
		j = datalen;
		p[j++] = pbc[0];    
		p[j] = '\0';
		if (xunchar(pbc[1]) == chk1(q))
		  break;
		else
		  p[--j] = '\0';
	    }
	    freerslot(k,r_slot);
	    if (k->what == W_RECV)
	      nak(k,k->r_seq,r_slot);
	    else
	      resend(k);
	    return(X_OK);
	}
	break;

      case 3:				/* Type 3, 16-bit CRC */
	crc = (xunchar(pbc[0]) << 12)
	  | (xunchar(pbc[1]) << 6)
	    | (xunchar(pbc[2]));
	ok = (crc == chk3(q,k));
#ifdef DEBUG
	if (ok && xerror()) {
	    ok = 0;
	    if (debug) fprintf(dp,"CRC ERROR INJECTED\n");
	} 
#endif /* DEBUG */
	if (!ok) {
#ifdef DEBUG
	    if (debug) fprintf(dp,"CRC ERROR %ld != %ld\n",crc, chk3(q,k));
#endif /* DEBUG */
	    if (t == 'E') {		/* Allow E packets to have type 1 */
		int j;
		j = datalen;
		p[j++] = pbc[0];    
		p[j++] = pbc[1];
		p[j] = '\0';
		if (xunchar(pbc[2]) == chk1(q))
		  break;
		else { j -=2; p[j] = '\0'; }
	    }
	    freerslot(k,r_slot);
	    if (k->what == W_RECV)
	      nak(k,k->r_seq,r_slot);
	    else
	      resend(k);
	    return(X_OK);
	}
    }
#ifdef DEBUG
    if (debug) {
	fprintf(dp,"PACKET OK\n");
	fprintf(dp,"Typ=%c\n",t);
	fprintf(dp,"Dat=%s\n",p);
    }
#endif /* DEBUG */

    if (t == 'E')
      return(X_ERROR);

    prev = k->r_seq - 1;		/* Get sequence of previous packet */
    if (prev < 0)
      prev = 63;

#ifdef DEBUG
    if (debug)
      fprintf(dp,"Seq=%d Prev=%d\n",seq,prev);
#endif /* DEBUG */

    if (seq == k->r_seq) {		/* Is this the packet we want? */
	k->ipktinfo[r_slot].rtr = 0;	/* Yes */
    } else {
        freerslot(k,r_slot);		/* No, discard it. */

        if (seq == prev) {              /* If it's the previous packet again */
#ifdef DEBUG
	    if (debug) {
		fprintf(dp,"PREVIOUS PACKET AGAIN: RETRIES=%d, LIMIT=%d\n",
			k->ipktinfo[r_slot].rtr,
			k->retry
			);
	    }
#endif /* DEBUG */
            if (k->ipktinfo[r_slot].rtr++ > k->retry) { /* Count retries */
                epkt("Too many retries", k); /* Too may */
                return(X_ERROR);	/* Give up */
            } else {			/* Otherwise */
		return(resend(k));	/* Send old outbound packet buffer */
            }
        } else if (k->what == W_RECV) {	/* Otherwise NAK the one we want */
	    return(nak(k,k->r_seq,r_slot));
	} else {			/* or whatever... */
	    return(resend(k));
	} 
    }
    if (k->what == W_SEND) {		/* Sending, check for ACK */
	if (t != 'Y')
	  return(resend(k));		/* It isn't */
	if (k->state == S_DATA) {	/* ACK to Data packet?*/
	    if (*p == 'X' || *p == 'Z') { /* Contains cancellation request? */
		/* NEED TO CLOSE FILE HERE TOO */
		if ((rc = spkt('Z',k->s_seq,0,(UCHAR *)0,k)) != X_OK)
		  return(rc);
		k->state = S_EOF;		/* Wait for ACK to EOF */
#if 0
		if (*p == 'Z')
		  while ((k->filelist)++) ;	/* Go to end of file list */
#endif
		k->state = S_EOF;		/* Wait for ACK to EOF */
	    }
	}
	freerslot(k,r_slot);		/* It is, free the ACK. */
    }

/* Now we have an incoming packet with the expected sequence number. */

#ifdef DEBUG
    if (debug) fprintf(dp,"GOT good %c-Packet\n",t);
#endif /* DEBUG */

    switch (k->state) {                 /* Kermit protocol state switcher */ 

      case S_INIT:			/* Got other Kermit's parameters */
      case S_EOF:			/* Got ACK to EOF packet */
	nxtpkt(k);			/* Get next packet number etc */
	if (k->state == S_INIT) {	/* Got ACK to S packet? */
	    spar(k,p,datalen);		/* Set negotiated parameters */
#ifdef DEBUG
	    if (debug) {
		fprintf(dp,"parity = '%c'\n", k->parity ?  k->parity : 'n');
		fprintf(dp,"ebqflg = %d\n", k->ebqflg);
		fprintf(dp,"ebq = '%c'\n", k->ebq);
	    }
#endif /* DEBUG */
	}
	k->sendfile = *(k->filelist)++;	/* Get next filename */
#ifdef DEBUG
	if (debug) fprintf(dp,"sendfile = [%s]\n",k->sendfile);
#endif /* DEBUG */
	if (!(k->sendfile)) k->sendfile = (UCHAR *)"";
	if (*(k->sendfile)) {		/* If there is one */
	    if ((rc = (k->openf)(k->sendfile,1,k)) != X_OK) /* Try to open */
	      return(rc);
	    encstr(k->sendfile,k);	/* Encode the name for transmission */
	    if ((rc = spkt('F',k->s_seq,-1,k->xdata,k)) != X_OK)
	      return(rc);		/* Send F packet */
	    k->state = S_FILE;		/* Wait for ACK */
	} else {			/* No more files - we're done */
	    if ((rc = spkt('B',k->s_seq,0,(UCHAR *)0,k)) != X_OK)
	      return(rc);		/* Send EOT packet */
	    k->state = S_EOT;		/* Wait for ACK */
	}
	k->r_seq = k->s_seq;		/* Sequence number of packet we want */
	return(X_OK);			/* Return to control program */

      case S_FILE:			/* Got ACK to F packet */
	nxtpkt(k);			/* Get next packet number etc */
	if (k->capas & CAP_AT) {	/* A-packets negotiated? */
	    if ((rc = sattr(k)) != X_OK) /* Yes, send Attribute packet */
	      return(rc);
	    k->state = S_ATTR;		/* And wait for its ACK */
	} else if (sdata(k) == 0) {	/* No A packets - send first data */
	    /* File is empty so send EOF packet */
	    if ((rc = spkt('Z',k->s_seq,0,(UCHAR *)0,k)) != X_OK)
	      return(rc);
	    k->state = S_EOF;		/* Wait for ACK to EOF */
	} else {			/* Sent some data */
	    k->state = S_DATA;		/* Wait for ACK to first data */
	}
	k->r_seq = k->s_seq;		/* Sequence number to wait for */
	return(X_OK);

      case S_ATTR:			/* Got ACK to A packet */
      case S_DATA:			/* Got ACK to D packet */
	nxtpkt(k);			/* Get next packet number */
	if (k->state == S_ATTR) {
	    /* CHECK ATTRIBUTE RESPONSE */
	    /* IF REJECTED do the right thing... */
	    k->state = S_DATA;
	}
	rc = sdata(k);			/* Send first or next data packet */
#ifdef DEBUG
	if (debug) fprintf(dp,"sdata(%d) = %d\n",k->s_seq,rc);
#endif /* DEBUG */
	if (rc == 0) {			/* If there was no data to send */
	    if ((rc = spkt('Z',k->s_seq,0,(UCHAR *)0,k)) != X_OK)
	      return(rc);		/* Send EOF */
	    k->state = S_EOF;		/* And wait for ACK */
	}				/* Otherwise stay in data state */
	k->r_seq = k->s_seq;		/* Sequence number to wait for */
	return(X_OK);

      case S_EOT:			/* Get ACK to EOT packet */
        return(X_DONE);			/* (or X_ERROR) */

      case R_WAIT:                      /* Waiting for the S packet */
        if (t == 'S') {                 /* Got it */
            spar(k,p,datalen);          /* Set parameters from it */
            rc = rpar(k,'Y');		/* ACK with my parameters */
#ifdef DEBUG
            if (debug) fprintf(dp,"rpar returns %d\n",rc);
#endif /* DEBUG */
            if (rc != X_OK)
              return(X_ERROR);          /* I/O error, quit. */
            k->state = R_FILE;          /* All OK, switch states */
        } else {                        /* Wrong kind of packet, send NAK */
            epkt("Unexpected packet type",k);
            rc = X_ERROR;
        }
        freerslot(k,r_slot);		/* Free packet slot */
        return(rc);

      case R_FILE:                      /* Want an F or B packet */
        if (t == 'F') {                 /* File name */
            if ((rc = decode(k, r, 0, p)) == X_OK) /* Decode and save */
              k->state = R_ATTR;        /* Switch to next state */
#ifdef DEBUG
            if (debug) fprintf(dp,"FILENAME=[%s], code=%d\n",r->filename,rc);
#endif /* DEBUG */
            if (rc == X_OK)              /* All OK so far */
              rc = ack(k, k->r_seq, r->filename); /* so ACK the F packet */
            else
              epkt("Filename error",k); /* Error decoding filename */

        } else if (t == 'B') {          /* Break, end of transaction */
            freerslot(k,r_slot);
            rc = (ack(k, k->r_seq, (UCHAR *)0) == X_OK) ? X_DONE : X_ERROR;

        } else {
            epkt("Unexpected packet type",k);
            rc = X_ERROR;
        }
        freerslot(k,r_slot);
        return(rc);

      case R_ATTR:                      /* Want A or D packet */
        if (t == 'A') {                 /* Attribute packet */
#ifdef F_AT
            gattr(p, r);                /* Read the attributes */
#endif /* F_AT */
            freerslot(k,r_slot);
            ack(k, k->r_seq, (UCHAR *) "Y"); /* Always accept the file */
            return(X_OK);
        } else if (t == 'D') {          /* First data packet */
            k->obufpos = 0;             /* Initialize output buffer */
            if ((rc = (*(k->openf))(r->filename, 2, k)) == X_OK) {
                k->state = R_DATA;      /* Switch to Data state */
                rc = decode(k, r, 1, p); /* Write out first data packet */
                freerslot(k,r_slot);
            } else {
                epkt("File refused or can't be opened", k);
                freerslot(k,r_slot);
                return(rc);
            }
            if (rc == X_OK)
              rc = ack(k, k->r_seq, (UCHAR *)0);
            else
              epkt("Error writing data", k);
            return(rc);
        } else {
            epkt("Unexpected packet type",k);
            return(X_ERROR);
        }
        break;

      case R_DATA:                      /* Want a D or Z packet */
        if (t == 'D') {                 /* Data */
            rc = decode(k, r, 1, p);	/* Decode it */
            freerslot(k,r_slot);
        } else if (t == 'Z') {          /* End of file */
            if (k->obufpos > 0) {       /* Flush output buffer */
                rc = writefile(k,k->obuf,k->obufpos);
                k->obufpos = 0;
            }
            if (((rc = (*(k->closef))(*p,2)) == X_OK) && (rc == X_OK))
              k->state = R_FILE;
            freerslot(k,r_slot);
        } else {
            epkt("Unexpected packet type",k);
            return(X_ERROR);
        }
        if (rc == X_OK)
          rc = ack(k, k->r_seq, (UCHAR *)0);
        else
          epkt(t == 'Z' ? "Can't close file" : "Error writing data",k);
        return(rc);

      case R_ERROR:                     /* Canceled from above */
      default:
        epkt(msg,k);
        return(X_ERROR);
    }
}

/* Utility routines */

UCHAR *
getrslot(struct k_data *k, short *n) {   /* Find a free packet buffer */
    register int i;
/*
  Note: We don't clear the retry count here.
  It is cleared only after the NEXT packet arrives, which
  indicates that the other Kermit got our ACK for THIS packet.
*/
    for (i = 0; i < P_WSLOTS; i++) {    /* Search */
        if (k->ipktinfo[i].len < 1) {
            *n = i;                     /* Slot number */
            k->ipktinfo[i].len = -1;	/* Mark it as allocated but not used */
            k->ipktinfo[i].seq = -1;
            k->ipktinfo[i].typ = SP;
            /* k->ipktinfo[i].rtr =  0; */  /* (see comment above) */
            k->ipktinfo[i].dat = (UCHAR *)0;
            return(&(k->ipktbuf[0][i]));
        }
    }   
    *n = -1;
    return((UCHAR *)0);
}

void                                    /* Initialize a window slot */
freerslot(struct k_data *k, short n) {
    k->ipktinfo[n].len = 0;		/* Packet length */
#ifdef COMMENT
    k->ipktinfo[n].seq = 0;		/* Sequence number */
    k->ipktinfo[n].typ = (char)0;	/* Type */
    k->ipktinfo[n].rtr = 0;		/* Retry count */
    k->ipktinfo[n].flg = 0;		/* Flags */
#endif /* COMMENT */
}

UCHAR *
getsslot(struct k_data *k, short *n) {   /* Find a free packet buffer */
#ifdef COMMENT
    register int i;
    for (i = 0; i < P_WSLOTS; i++) {    /* Search */
        if (k->opktinfo[i].len < 1) {
            *n = i;                     /* Slot number */
            k->opktinfo[i].len = -1;	/* Mark it as allocated but not used */
            k->opktinfo[i].seq = -1;
            k->opktinfo[i].typ = SP;
            k->opktinfo[i].rtr =  0;
            k->opktinfo[i].dat = (UCHAR *)0;
            return(&(k->opktbuf[0][i]));
        }
    }   
    *n = -1;
    return((UCHAR *)0);
#else
    *n = 0;
    return(k->opktbuf);
#endif /* COMMENT */
}

void                                    /* Initialize a window slot */
freesslot(struct k_data * k, short n) {
    k->opktinfo[n].len = 0;		/* Packet length */
    k->opktinfo[n].seq = 0;		/* Sequence number */
    k->opktinfo[n].typ = (char)0;	/* Type */
    k->opktinfo[n].rtr = 0;		/* Retry count */
    k->opktinfo[n].flg = 0;		/* Flags */
}

/*  C H K 1  --  Compute a type-1 Kermit 6-bit checksum.  */

int
chk1(UCHAR *pkt) {
    register unsigned int chk;
    chk = chk2(pkt);
    chk = (((chk & 0300) >> 6) + chk) & 077;
    return((int) chk);
}

/*  C H K 2  --  Numeric sum of all the bytes in the packet, 12 bits.  */

unsigned int
chk2(UCHAR *pkt) {
    register long chk; register unsigned int m;
    for (chk = 0; *pkt != '\0'; pkt++)
      chk += *pkt;
    return((unsigned int) (chk & 07777));
}

/*  C H K 3  --  Compute a type-3 Kermit block check.  */
/*
 Calculate the 16-bit CRC-CCITT of a null-terminated string using a lookup 
 table.  Assumes the argument string contains no embedded nulls.
*/
unsigned int
chk3(UCHAR *pkt, struct k_data * k) {
    register long c, crc;
    for (crc = 0; *pkt != '\0'; pkt++) {
        c = crc ^ (long)(*pkt);
        crc = (crc >> 8) ^ (k->crcta[(c & 0xF0) >> 4] ^ k->crctb[c & 0x0F]);
    }
    return((unsigned int) (crc & 0xFFFF));
}

/*   S P K T  --  Send a packet.  */
/*
  Call with packet type, sequence number, data length, data, Kermit struct.
  Returns:
    X_OK on success
    X_ERROR on i/o error
*/
int
spkt(char typ, short seq, int len, UCHAR * data, struct k_data * k) {

    unsigned int crc;                   /* For building CRC */
    int i, j, lenpos, m, n, x;		/* Workers */
    UCHAR * s, * buf;

    if (len < 0) {			/* Calculate data length ourselves? */
	len = 0;
	s = data;
	while (*s++) len++;
    }
    buf = k->opktbuf;			/* Where to put packet (FOR NOW) */

    i = 0;                              /* Packet buffer position */
    buf[i++] = k->s_soh;		/* SOH */
    lenpos = i++;			/* Remember this place */
    buf[i++] = tochar(seq);		/* Sequence number */
    buf[i++] = typ;			/* Packet type */
    j = len + k->bct;
    if ((len + k->bct + 2) > 94) {	/* If long packet */
	buf[lenpos] = tochar(0);	/* Put blank in LEN field */
	buf[i++] = tochar(j / 95);	/* Make extended header: Big part */
	buf[i++] = tochar(j % 95);	/* and small part of length. */
        buf[i] = NUL;			/* Terminate for header checksum */
        buf[i++] = tochar(chk1(&buf[lenpos])); /* Insert header checksum */
    } else {				/* Short packet */
	buf[lenpos] = tochar(j+2);	/* Single-byte length in LEN field */
    }
    if (data)                           /* Copy data, if any */
      for ( ; len--; i++)
        buf[i] = *data++;
    buf[i] = '\0';

    switch (k->bct) {                   /* Add block check */
      case 1:                           /* 1 = 6-bit chksum */
	buf[i++] = tochar(chk1(&buf[lenpos]));
        break;
      case 2:                           /* 2 = 12-bit chksum */
        j = chk2(&buf[lenpos]);
        buf[i++] = (unsigned)tochar((j >> 6) & 077);
        buf[i++] = (unsigned)tochar(j & 077);
        break;
      case 3:                           /* 3 = 16-bit CRC */
        crc = chk3(&buf[lenpos],k);
        buf[i++] = (unsigned)tochar(((crc & 0170000)) >> 12);
        buf[i++] = (unsigned)tochar((crc >> 6) & 077);
        buf[i++] = (unsigned)tochar(crc & 077);
        break;
    }
    buf[i++] = k->s_eom;		/* Packet terminator */
    buf[i] = '\0';			/* String terminator */
    k->s_seq = seq;                     /* Remember sequence number */

    k->opktlen = i;			/* Remember length for retransmit */

#ifdef DEBUG
/* CORRUPT THE PACKET SENT BUT NOT THE ONE WE SAVE */
    if (xerror()) {
	UCHAR p[P_PKTLEN+8];
	strncpy((char *)p,(const char *)buf,P_PKTLEN);
	p[i-2] = 'X';
#ifdef DEBUG
	if (debug) fprintf(dp,"XPKT[%s]\n",&p[1]);
#endif /* DEBUG */
	return((*(k->txd))(p,k->opktlen,k->parity)); /* Send it. */
    }
#endif /* DEBUG */

#ifdef DEBUG
    if (debug) fprintf(dp,"SPKT[%s]\n",&buf[1]);
#endif /* DEBUG */

    return((*(k->txd))(buf,k->opktlen,k->parity)); /* Send it. */
}

/*  N A K  --  Send a NAK (negative acknowledgement)  */

int
nak(struct k_data * k, short seq, short slot) {
    int rc;
    rc = spkt('N', seq, 0, (UCHAR *)0, k);
    if (k->ipktinfo[slot].rtr++ > k->retry)
      rc = X_ERROR;
    return(rc);
}

/*  A C K  --  Send an ACK (positive acknowledgement)  */

int
ack(struct k_data * k, short seq, UCHAR * text) {
    int len, rc;
    len = 0;
    if (text) {                         /* Get length of data */
        UCHAR *p;
        p = text;
        for ( ; *p++; len++) ;
    }
    rc = spkt('Y', seq, len, text, k);  /* Send the packet */
    if (rc == X_OK)                     /* If OK */
      k->r_seq = (k->r_seq + 1) % 64;   /* bump the packet number */
    return(rc);
}

/*  S P A R  --  Set parameters requested by other Kermit  */

void
spar(struct k_data * k, UCHAR *s, int datalen) {
    int x, y;
    UCHAR c;

    s--;                                /* Line up with field numbers. */

    if (datalen >= 1)                   /* Max packet length to send */
      k->s_maxlen = xunchar(s[1]);

    if (datalen >= 2)                   /* Timeout on inbound packets */
      k->r_timo = xunchar(s[2]);

    /* No padding */

    if (datalen >= 5)                   /* Outbound Packet Terminator */
      k->s_eom = xunchar(s[5]);

    if (datalen >= 6)                   /* Incoming control prefix */
      k->r_ctlq = s[6];

    if (datalen >= 7) {                 /* 8th bit prefix */
        k->ebq = s[7];
        if ((s[7] > 32 && s[7] < 63) || (s[7] > 95 && s[7] < 127)) {
	    if (!k->parity)		/* They want it */
	      k->parity = 1;		/* Set parity to something nonzero */
	    k->ebqflg = 1;
	} else if (s[7] == 'Y' && k->parity) {
	    k->ebqflg = 1;
	    k->ebq = '&';
	} else if (s[7] == 'N') {
	    /* WHAT? */
	}
    }
    if (datalen >= 8) {                 /* Block check */
        k->bct = s[8] - '0';
        if ((k->bct < 1) || (k->bct > 3)) k->bct = 1;
    }
    if (datalen >= 9) {                 /* Repeat counts */
        if ((s[9] > 32 && s[9] < 63) || (s[9] > 95 && s[9] < 127)) {
            k->rptq = s[9];
            k->rptflg = 1;
        }
    }
    if (datalen >= 10) {                /* Capability bits */
        x = xunchar(s[10]);

#ifdef F_LP                             /* Long packets */
        if (!(x & CAP_LP))
#endif /* F_LP */
          k->capas &= ~CAP_LP;

#ifdef F_SW                             /* Sliding Windows */
        if (!(x & CAP_SW))
#endif /* F_SW */
          k->capas &= ~CAP_SW;

#ifdef F_AT                             /* Attributes */
        if (!(x & CAP_AT))
#endif /* F_AT */
          k->capas &= ~CAP_AT;

#ifdef F_RS                             /* Recovery */
        if (!(x & CAP_RS))
#endif /* F_RS */
          k->capas &= ~CAP_RS;

#ifdef F_LS                             /* Locking shifts */
        if (!(x & CAP_LS))
#endif /* F_LS */
          k->capas &= ~CAP_LS;

        /* In case other Kermit sends addt'l capas fields ... */

        for (y = 10; (xunchar(s[y]) & 1) && (datalen >= y); y++) ;
    }

#ifdef F_LP                             /* Long Packets */
    if (k->capas & CAP_LP) {
        if (datalen > y+1) {
            x = xunchar(s[y+2]) * 95 + xunchar(s[y+3]);
            k->s_maxlen = (x > P_PKTLEN) ? P_PKTLEN : x;
            if (k->s_maxlen < 10)
              k->s_maxlen = 60;
        }
    }
#endif /* F_LP */
    
#ifdef F_SW
    if (k->capas & CAP_SW) {
        if (datalen > y) {
            x = xunchar(s[y+1]);
            k->window = (x > P_WSLOTS) ? P_WSLOTS : x;
            if (k->window < 1)          /* Watch out for bad negotiation */
              k->window = 1;
            if (k->window > 1)
              if (k->window > k->retry)   /* Retry limit must be greater */
                k->retry = k->window + 1; /* than window size. */
        }
    }
#endif /* F_SW */
}

/*  R P A R  --  Send my parameters to other Kermit  */

int
rpar(struct k_data * k, char type) {
    UCHAR *d;
    short b;
    int rc, len;

    d = k->ack_s;                       /* Where to put it */
    d[ 0] = tochar(94);                 /* Maximum short-packet length */
    d[ 1] = tochar(k->s_timo);          /* When I want to be timed out */
    d[ 2] = tochar(0);                  /* How much padding I need */
    d[ 3] = ctl(0);                     /* Padding character I want */
    d[ 4] = tochar(k->r_eom);           /* End-of message character I want */
    d[ 5] = k->s_ctlq;                  /* Control prefix I send */
    if ((k->ebq == 'Y') && (k->parity)) /* 8th-bit prefix */
      d[ 6] = k->ebq = '&';           /* I need to request it */
    else                                /* else just agree with other Kermit */
      d[ 6] = k->ebq;
    d[ 7] = k->bct + '0';               /* Block check type */
    d[ 8] = k->rptq;			/* Repeat prefix */
    d[ 9] = tochar(k->capas);           /* Capability bits */
    d[10] = tochar(k->window);          /* Window size */

#ifdef F_LP
    d[11] = tochar(k->r_maxlen / 95);   /* Long packet size, big part */
    d[12] = tochar(k->r_maxlen % 95);   /* Long packet size, little part */
    d[13] = '\0';                        /* Terminate the init string */
    len = 13;
#else
    d[11] = '\0';
    len = 11;
#endif /* F_LP */

    b = k->bct;
    k->bct = 1;                         /* Always use block check type 1 */
    switch (type) {
      case 'Y':				/* This is an ACK for packet 0 */
	rc = ack(k,0,d);
	break;
      case 'S':				/* It's an S packet */
	rc = spkt('S', 0, len, d, k);
	break;
      default:
	rc = -1;
    }
    k->bct = b;
    return(rc);                         /* Pass along return code. */
}

/*  D E C O D E  --  Decode data field of Kermit packet - binary mode only */
/*
  Call with:
    k = kermit data structure
    r = kermit response structure
    f = function code
      0 = decode filename
      1 = decode file data    
    inbuf = pointer to packet data to be decoded
  Returns:
    X_OK on success
    X_ERROR if output function fails
*/
int
decode(struct k_data * k, struct k_response * r, short f, UCHAR *inbuf) {

    register unsigned int a, a7;        /* Current character */
    unsigned int b8;                    /* 8th bit */
    int rpt;                            /* Repeat count */
    UCHAR *p;

    rpt = 0;                            /* Initialize repeat count. */
    if (f == 0)                         /* Output function... */
      p = r->filename;

    while ((a = *inbuf++ & 0xFF) != '\0') { /* Character loop */
        if (k->rptflg && a == k->rptq) { /* Got a repeat prefix? */
            rpt = xunchar(*inbuf++ & 0xFF); /* Yes, get the repeat count, */
            a = *inbuf++ & 0xFF;        /* and get the prefixed character. */
        }
        b8 = 0;                         /* 8th-bit value */
        if (k->parity && (a == k->ebq)) { /* Have 8th-bit prefix? */
            b8 = 0200;                  /* Yes, flag the 8th bit */
            a = *inbuf++ & 0x7F;        /* and get the prefixed character. */
        }
        if (a == k->r_ctlq) {           /* If control prefix, */
            a  = *inbuf++ & 0xFF;       /* get its operand */
            a7 = a & 0x7F;              /* and its low 7 bits. */
            if ((a7 >= 0100 && a7 <= 0137) || a7 == '?') /* Controllify */
              a = ctl(a);               /* if in control range. */
        }
        a |= b8;                        /* OR in the 8th bit */

        if (rpt == 0) rpt = 1;          /* If no repeats, then one */

        for (; rpt > 0; rpt--) {        /* Output the char 'rpt' times */
            if (f == 0) {
                *p++ = (UCHAR) a;       /* to memory */
            } else {                    /* or to file */
                int rc;                 /* buffered for efficiency */
                k->obuf[k->obufpos++] = (UCHAR) a; /* Deposit the byte */
                if (k->obufpos == k->obuflen) { /* Buffer full? */
                    rc = writefile(k,k->obuf,k->obuflen); /* Dump it. */
                    k->obufpos = 0;
                }
            }
        }
    }
    if (f == 0)                         /* If writing to memory */
      *p = '\0';			/* terminate the string */
    return(X_OK);
}

ULONG					/* Convert decimal string to number  */
stringnum(UCHAR *s) {
    long n;
    n = 0L;
    while (*s == SP)
      s++;
    while(*s >= '0' && *s <= '9')
      n = n * 10 + (*s++ - '0');
    return(n);
}

UCHAR *
numstring(ULONG n, UCHAR * buf, int buflen) { /* Convert number to string */
    int k, x;
    buf[buflen - 1] = '\0';
    for (k = buflen - 2; k > 0; k--) {
	x = n % 10L;
	buf[k] = x + '0';
	n /= 10L;
	if (!n)
	  break;
    }
    if (n) {
#ifdef DEBUG
	if (debug) fprintf(dp,"numstring n=%d buf=[%s]\n",n,&buf[k]);
#endif /* DEBUG */
	return((UCHAR *)0);
    }
    if (k > 0) {
	UCHAR * p, * s;
	s = &buf[k];
	p = buf;
	while ((*p++ = *s++)) ;
	*(p-1) = '\0';
    }
#ifdef DEBUG
    if (debug) fprintf(dp,"numstring final buf=[%s]\n",buf);
#endif /* DEBUG */
    return((UCHAR *)buf);
}

#ifdef F_AT

/*  G A T T R  --  Read incoming attributes  */

#define SIZEBUFL 32                     /* For number conversions */

void
gattr(UCHAR * s, struct k_response * r) {
    long fsize, fsizek;                 /* File size */
    UCHAR c;                            /* Workers */
    int aln, i;

    UCHAR sizebuf[SIZEBUFL];

    r->filedate[0] = '\0';               /* Initialize results */ 
    r->filesize = -1L;

    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 < SIZEBUFL); i++) /* Copy it */
              sizebuf[i] = *s++;
            sizebuf[i] = '\0';           /* Terminate with null */
            if (i < aln) s += (aln - i); /* If field was too long for buffer */
            fsizek = stringnum(sizebuf);  /* Convert to number */
            break;

          case '#':                     /* File creation date */
            for (i = 0; (i < aln) && (i < DATE_MAX); i++)
              r->filedate[i] = *s++;    /* Copy it into a static string */
            if (i < aln) s += (aln - i);
            r->filedate[i] = '\0';
            break;

          case '1':                     /* File length in bytes */
            for (i = 0; (i < aln) && (i < SIZEBUFL); i++) /* Copy it */
              sizebuf[i] = *s++;
            sizebuf[i] = '\0';           /* Terminate with null */
            if (i < aln) s += (aln - i);
            fsize = stringnum(sizebuf); /* Convert to number */
            break;
        }
    }
    if (fsize > -1L) {          /* Remember the file size */
        r->filesize = fsize;            
    } else if (fsizek > -1L) {
        r->filesize = fsizek * 1024L;
    }

#ifdef DEBUG
    if (debug) {
	fprintf(dp,"gattr r->filesize=%ld\n",r->filesize);
	fprintf(dp,"gattr r->filedate=[%s]\n",r->filedate);
    }
#endif /* DEBUG */
}
#endif /* F_AT */

#define ATTRLEN 48

int
sattr(struct k_data *k) {		/* Build and send A packet */
    int i, x, aln;
    short filetype;
    long filelength;
    UCHAR datebuf[24], * p;

    filetype = k->binary;
    filelength = fileinfo(k->sendfile,datebuf,24,&filetype,k->xfermode);
    k->binary = filetype;

#ifdef DEBUG
    if (debug) {
	fprintf(dp,"sattr filename: %s\n",k->sendfile);
	fprintf(dp,"sattr filelength: %ld\n",filelength);
	fprintf(dp,"sattr binary: %d\n",k->binary);
    }
#endif /* DEBUG */

    i = 0;

    k->xdata[i++] = '"';
    if (k->binary) {			/* Binary */
	k->xdata[i++] = tochar(2);	/*  Two characters */
	k->xdata[i++] = 'B';		/*  B for Binary */
	k->xdata[i++] = '8';		/*  8-bit bytes (note assumption...) */
    } else {				/* Text */
	k->xdata[i++] = tochar(3);	/*  Three characters */
	k->xdata[i++] = 'A';		/*  A = (extended) ASCII with CRLFs */
	k->xdata[i++] = 'M';		/*  M for carriage return */
	k->xdata[i++] = 'J';		/*  J for linefeed */
	k->xdata[i++] = '*';		/* Encoding */
	k->xdata[i++] = tochar(1);	/* Length of value is 1 */
	k->xdata[i++] = 'A';		/* A for ASCII */
    }
    if (filelength > -1L) {		/* File length in bytes */
	UCHAR lenbuf[16];
	p = numstring(filelength,lenbuf,16);
#ifdef DEBUG
	if (debug) fprintf(dp,"sattr numstring: [%s]\n",p);
#endif /* DEBUG */

	if (p) {
	    for (x = 0; p[x]; x++) ;	/* Get length of length string */
	    if (i + x < ATTRLEN - 3) {	/* Don't overflow buffer */
		k->xdata[i++] = '1';	/* Length-in-Bytes attribute */
		k->xdata[i++] = tochar(x);
		while (*p)
		  k->xdata[i++] = *p++;
	    }
	}
    }
    if (datebuf[0]) {			/* File modtime */
	p = datebuf;
	for (x = 0; p[x]; x++) ;	/* Length of modtime */
	if (i + x < ATTRLEN - 3) {	/* If it will fit */
	    k->xdata[i++] = '#';	/* Add modtime attribute */
	    k->xdata[i++] = tochar(x);	/* Its length */
	    while (*p)			/* And itself */
	      k->xdata[i++] = *p++;
	}	
    }
    k->xdata[i++] = '@';		/* End of Attributes */
    k->xdata[i++] = ' ';
    k->xdata[i] = '\0';			/* Terminate attribute string */
#ifdef DEBUG
    if (debug) fprintf(dp,"sattr k->xdata: [%s]\n",k->xdata);
#endif /* DEBUG */
    return(spkt('A',k->s_seq,-1,k->xdata,k));
}

int
getpkt(struct k_data *k) {		/* Fill a packet from file */
    int i, next, rpt, maxlen;
    static int c;			/* PUT THIS IN STRUCT */

#ifdef DEBUG
    if (debug) {
	fprintf(dp,"getpkt k->s_first: %d\n",k->s_first);
	fprintf(dp,"getpkt k->s_remain=[%s]\n",k->s_remain);
    }
#endif /* DEBUG */

    maxlen = k->s_maxlen - 4;
    if (k->s_first == 1) {		/* If first time thru...  */
	k->s_first = 0;			/* don't do this next time, */
	k->s_remain[0] = '\0';		/* discard any old leftovers. */
	if (k->istring) {		/* Get first byte. */
	    c = *(k->istring)++;	/* Of memory string... */
	    if (!c) c = -1;
	} else {			/* or file... */
	    c = zgetc();
	}
	if (c < 0) {			/* Watch out for empty file. */
#ifdef DEBUG
	if (debug) fprintf(dp,"getpkt first c=%d\n",c);
#endif /* DEBUG */
	    k->s_first = -1;
	    return(k->size = 0);
	}
#ifdef DEBUG
	if (debug) fprintf(dp,"getpkt first c='%c'\n",(char)c);
#endif /* DEBUG */
    } else if (k->s_first == -1 && !k->s_remain[0]) { /* EOF from last time? */
        return(k->size = 0);
    }
    for (k->size = 0;
	 (k->xdata[k->size] = k->s_remain[k->size]) != '\0';
	 (k->size)++)
      ;
    k->s_remain[0] = '\0';
    if (k->s_first == -1)
      return(k->size);

    rpt = 0;				/* Initialize repeat counter. */
    while (k->s_first > -1) {		/* Until end of file or string... */
	if (k->istring) {
	    next = *(k->istring)++;
	    if (!next) next = -1;
	} else {
	    next = zgetc();
	}
#ifdef COMMENT
#ifdef DEBUG
	if (debug) {
	    if (next < 0)
	      fprintf(dp,"getpkt next c=%d\n",c);
	    else
	      if (debug) fprintf(dp,"getpkt next c='%c'\n",c);
	}
#endif /* DEBUG */
#endif /* COMMENT */

	if (next < 0) k->s_first = -1;	/* If none, we're at EOF. */
        k->osize = k->size;		/* Remember current size. */
        encode(c,next,k);		/* Encode the character. */
	/* k->xdata[k->size] = '\0'; */
	c = next;			/* Old next char is now current. */

        if (k->size == maxlen)		/* Just at end, done. */
	  return(k->size);

        if (k->size > maxlen) {		/* Past end, must save some. */
            for (i = 0;
		 (k->s_remain[i] = k->xdata[(k->osize)+i]) != '\0';
		 i++)
	      ;
            k->size = k->osize;
            k->xdata[k->size] = '\0';
            return(k->size);		/* Return size. */
        }
    }
    return(k->size);			/* EOF, return size. */
}

int
sdata(struct k_data *k) {		/* Send a data packet */
    int len, rc;
#ifdef COMMENT
    if (cx || cz) {			/* Interruption not implmented yet */
	if (debug)
	  fprintf(db,"sdata interrupted cx = %d cz = %d\n",cx,cz);
	failure = 1;
	return(0);
    }
#endif /* COMMENT */
    len = getpkt(k);			/* Fill data field from input file */
#ifdef DEBUG
    if (debug) fprintf(dp,"sdata getpkt = %d\n",len);
#endif /* DEBUG */
    if (len < 1)
      return(0);
    rc = spkt('D',k->s_seq,len,k->xdata,k); /* Send the packet */
#ifdef DEBUG
    if (debug) fprintf(dp,"sdata spkt = %d\n",rc);
#endif /* DEBUG */
    return((rc == X_ERROR) ? rc : len);
}

/*  E P K T  --  Send a (fatal) Error packet with the given message  */

void
epkt(char * msg, struct k_data * k) {
    k->bct = 1;
    (void) spkt('E', 0, -1, (UCHAR *) msg, k);
}

int
encstr(UCHAR * s, struct k_data * k) {	/* Fill a packet from string s. */
    k->s_first = 1;			/* Start lookahead. */
    k->istring = s;			/* Set input string pointer */
    getpkt(k);				/* Fill a packet */
    k->istring = (UCHAR *)0;		/* Reset input string pointer */
    k->s_first = 1;			/* "Rewind" */
    return(k->size);			/* Return data field length */
}

/* Decode packet data into a string */

void
decstr(UCHAR * s, struct k_data * k, struct k_response * r) {
    k->ostring = s;			/* Set output string pointer  */
    (void) decode(k, r, 0, s);
    *(k->ostring) = '\0';		/* Terminate with null */
    k->ostring = (UCHAR *)0;		/* Reset output string pointer */
}

void
encode(int a, int next, struct k_data * k) { /* Encode character into packet */
    int a7, b8, maxlen;

    maxlen = k->s_maxlen - 4;
    if (k->rptflg) {			/* Doing run-length encoding? */
	if (a == next) {		/* Yes, got a run? */
	    if (++(k->s_rpt) < 94) {	/* Yes, count. */
		return;
	    } else if (k->s_rpt == 94) { /* If at maximum */
	    	k->xdata[(k->size)++] = k->rptq; /* Emit prefix, */
		k->xdata[(k->size)++] = tochar(k->s_rpt); /* and count, */
		k->s_rpt = 0;		/* and reset counter. */
	    }
	} else if (k->s_rpt == 1) {	/* Run broken, only two? */
	    k->s_rpt = 0;		/* Yes, do the character twice */
	    encode(a,-1,k);		/* by calling self recursively. */
	    if (k->size <= maxlen)	/* Watch boundary. */
	      k->osize = k->size;
	    k->s_rpt = 0;		/* Call self second time. */
	    encode(a,-1,k);
	    return;
	} else if (k->s_rpt > 1) {	/* Run broken, more than two? */
	    k->xdata[(k->size)++] = k->rptq; /* Yes, emit prefix and count */
	    k->xdata[(k->size)++] = tochar(++(k->s_rpt));
	    k->s_rpt = 0;		/* and reset counter. */
	}
    }
    a7 = a & 127;			/* Get low 7 bits of character */
    b8 = a & 128;			/* And "parity" bit */

    if (k->ebqflg && b8) {		/* If doing 8th bit prefixing */
	k->xdata[(k->size)++] = k->ebq; /* and 8th bit on, insert prefix */
	a = a7;				/* and clear the 8th bit. */
    }
    if (a7 < 32 || a7 == 127) {		   /* If in control range */
        k->xdata[(k->size)++] = k->s_ctlq; /* insert control prefix */
        a = ctl(a);			 /* and make character printable. */
    } else if (a7 == k->s_ctlq)		 /* If data is control prefix, */
      k->xdata[(k->size)++] = k->s_ctlq; /* prefix it. */
    else if (k->ebqflg && a7 == k->ebq)  /* If doing 8th-bit prefixing, */
      k->xdata[(k->size)++] = k->s_ctlq; /* ditto for 8th-bit prefix. */
    else if (k->rptflg && a7 == k->rptq) /* If doing run-length encoding, */
      k->xdata[(k->size)++] = k->s_ctlq; /* ditto for repeat prefix. */

    k->xdata[(k->size)++] = a;		/* Finally, emit the character. */
    k->xdata[(k->size)] = '\0';		/* Terminate string with null. */
#ifdef COMMENT
#ifdef DEBUG
    if (debug) fprintf(dp,"encode = [%s]\n",k->xdata);
#endif /* DEBUG */
#endif /* COMMENT */
}

int
nxtpkt(struct k_data * k) {		/* Get next packet to send */
#ifdef COMMENT
    short s_slot;
    k->s_seq = (k->s_seq + 1) & 63;	/* Next sequence number */
    k->xdata = getsslot(k,&s_slot);
    return(s_slot);
#else
    k->s_seq = (k->s_seq + 1) & 63;	/* Next sequence number */
    k->xdata = k->xdatabuf;
    return(0);
#endif /* COMMENT */
}

#ifdef DEBUG
int xerror() {
    unsigned int x;
    if (!errorrate)
      return(0);
    x = rand() % 100;
    if (debug) fprintf(dp,"RANDOM=%d ERROR=%d\n",x,(x < errorrate));
    return(x < errorrate);
}
#endif /* DEBUG */

int
resend(struct k_data * k) {
    UCHAR * buf;
    if (!k->opktlen)			/* Nothing to resend */
      return(X_OK);
    buf = k->opktbuf;
#ifdef DEBUG
    if (debug) fprintf(dp,">PKT[%s]\n",&buf[1]);
#endif /* DEBUG */
    return((*(k->txd))(buf,k->opktlen,k->parity));
}
