// todo:
// 1. should we send an I packet before the R-00 packet?
// 2. commands to exit the server and/or logout from the session

#define KERMIT_C
/*
 * EKSW: Embedded Kermit with true sliding windows 
 * protocol module Version: 0.94
 * Most Recent Update: March 30, 2010
 * John Dunlap
 * 
 * Author: Frank da Cruz. Copyright (C) 1995, 2004, 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.
 * 
 * Warning: you cannot use debug() in any routine whose argument list does
 * not include "struct k_data *k".  Thus most routines in this module
 * include this arg, even if they don't use it. 
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * - Neither the name of Columbia University nor the names of its contributors
 *   may be used to endorse or promote products derived from this software
 *   without specific prior written permission.
 *  
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

# if DEBUG
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
# endif

#include "cdefs.h"              /* C language defs for all modules */
#include "debug.h"              /* Debugging */
#include "kermit.h"             /* Kermit protocol definitions */

#define USE_ZGETC_MACRO
#ifdef USE_ZGETC_MACRO
#define zgetc() \
((--(k->zincnt))>=0)?((int)(*(k->zinptr)++)&0xff):(*(k->readf))(k)
#else // USE_ZGETC_MACRO
STATIC int
zgetc (struct k_data *k)
{
   UCHAR *ptr;
   int kar;

   k->zincnt--;
   if (k->zincnt >= 0)
   {
      ptr = k->zinptr;
      k->zinptr++;
      kar = (*ptr) & 0xFF;
   }
   else
   {
      kar = (*(k->readf)) (k);
   }
   return (kar);
}
#endif // USE_ZGETC_MACRO

/*
 * See cdefs.h for meaning of STATIC, ULONG, and UCHAR 
 */

STATIC ULONG stringnum (UCHAR *, struct k_data *);
STATIC UCHAR *numstring (ULONG, UCHAR *, int, struct k_data *);
STATIC int spkt (char, short, int, UCHAR *, struct k_data *);
STATIC int ack (struct k_data *, short, UCHAR * text);
STATIC int nak (struct k_data *, short, short);
STATIC int chk1 (UCHAR *, struct k_data *);
STATIC USHORT chk2 (UCHAR *, struct k_data *);
STATIC USHORT chk3 (UCHAR *, struct k_data *);

STATIC void spar (struct k_data *, UCHAR *, int);
STATIC int rpar (struct k_data *, char);
STATIC int decode (struct k_data *, struct k_response *, short, UCHAR *,
                   int rslot);

STATIC int gattr (struct k_data *, UCHAR *, struct k_response *);
STATIC int sattr (struct k_data *, struct k_response *);

STATIC int sdata (struct k_data *, struct k_response *);

STATIC void epkt (char *, struct k_data *);
STATIC int getpkt (struct k_data *, struct k_response *);
STATIC int encstr (UCHAR *, struct k_data *, struct k_response *);
STATIC void encode (int, int, struct k_data *);
STATIC short nxtpkt (struct k_data *);
STATIC int resend (struct k_data *, short seq);
STATIC int nused_sslots (struct k_data *);
STATIC int nused_rslots (struct k_data *);

#if 0
STATIC void decstr (UCHAR *, struct k_data *, struct k_response *);
STATIC short prevpkt (struct k_data *);
#endif

STATIC short earliest_sseq (struct k_data *k);

#ifdef DEBUG
int xerror (void);
STATIC void show_sslots (struct k_data *);
STATIC void show_rslots (struct k_data *);
#endif /* DEBUG */

STATIC int test_rslots (struct k_data *);

STATIC short
earliest_rseq (struct k_data *k)
{
   short slot;
   short seq;
   short seq_ref = -1;
   short seq_rot;
   short seq_rot_oldest = -1;
   short seq_oldest = -1;

   for (slot = 0; slot < k->wslots; slot++)
   {
      seq = k->ipktinfo[slot].seq;
      if (seq >= 0 && seq < 64)
      {
         seq_ref = seq;
         seq_oldest = seq;
         seq_rot_oldest = 0;
      }
   }

   if (seq_ref != -1)
   {
      for (slot = 0; slot < k->wslots; slot++)
      {
         seq = k->ipktinfo[slot].seq;
         if (seq >= 0 && seq < 64)
         {
            seq_rot = seq - seq_ref;
            if (seq_rot < -31)
               seq_rot += 64;
            if (seq_rot > 31)
               seq_rot -= 64;

            if (seq_rot < seq_rot_oldest)
            {
               seq_rot_oldest = seq_rot;
               seq_oldest = seq;
            }
         }
      }
   }

   debug (DB_LOG, "EARLIEST_RSEQ", 0, seq_oldest);
   return (seq_oldest);
}

STATIC short
latest_rseq (struct k_data *k)
{
   short slot;
   short seq;
   short seq_ref = -1;
   short seq_rot;
   short seq_rot_newest = -1;
   short seq_newest = -1;

   for (slot = 0; slot < k->wslots; slot++)
   {
      seq = k->ipktinfo[slot].seq;
      if (seq >= 0 && seq < 64)
      {
         seq_ref = seq;
         seq_newest = seq;
         seq_rot_newest = 0;
      }
   }

   if (seq_ref != -1)
   {
      for (slot = 0; slot < k->wslots; slot++)
      {
         seq = k->ipktinfo[slot].seq;
         if (seq >= 0 && seq < 64)
         {
            seq_rot = seq - seq_ref;
            if (seq_rot < -31)
               seq_rot += 64;
            if (seq_rot > 31)
               seq_rot -= 64;

            if (seq_rot > seq_rot_newest)
            {
               seq_rot_newest = seq_rot;
               seq_newest = seq;
            }
         }
      }
   }

   debug (DB_LOG, "LATEST_RSEQ", 0, seq_newest);
   return (seq_newest);
}

// nak_oldest_unacked() never finds anything because
// we ACK as soon as we put each packet into its slot.
// Also, rseq for short packets is unvetted and for long packets
// the only test is a simple checksum so it's safer
// to time out.  For Iridium, there are no bad bytes so
// the point is moot.
#undef USE_NAK_OLDEST_UNACKED
#ifdef USE_NAK_OLDEST_UNACKED
STATIC int
nak_oldest_unacked (struct k_data *k, short rseq)
{
   short slot;
   short seq;
   short age;
   short oldest_seq = -1;
   short oldest_age = -99;
   short oldest_rslot = -1;
   int flg;

   for (slot = 0; slot < k->wslots; slot++)
   {
      seq = k->ipktinfo[slot].seq;
      flg = k->ipktinfo[slot].flg;
      if (seq >= 0 && seq < 64 && flg == 0)
      {
         age = k->s_seq - seq;
         if (age < 0)
            age += 64;
         if (age > 63)
            age -= 64;

         if (age > oldest_age)
         {
            oldest_rslot = slot;
            oldest_age = age;
            oldest_seq = seq;
         }
      }
   }

   debug (DB_LOG, "NAK_OLDEST_UNACKED oldest_seq", 0, oldest_seq);
   debug (DB_LOG, "NAK_OLDEST_UNACKED rseq", 0, rseq);

   if (oldest_seq != -1)
   {
      nak (k, oldest_seq, oldest_rslot);
//    k->anseq = oldest_seq;
      return (X_OK);
   }
   else
   {
//    nak (k, (k->anseq+1)&63, -1);
//    nak (k, k->anseq, -1);
//    nak (k, rseq, -1);
      return (X_OK);
   }
}
#endif /* USE_NAK_OLDEST_UNACKED */

STATIC int
handle_good_rpkt (struct k_data *k, struct k_response *r,
                  short rseq, UCHAR * pbuf)
{
   // true sliding windows for receive see p. 292
   short wsize = k->wslots;     // negotiated window size
   short high = -1;             // latest table entry (chronlogically)
   short low = -1;              // earliest possible table entry
   short eseq = -1;             // expected sequence number
// short psn = rseq;            // just-arrived packet sequence number

   short rslot;
   short nused;
   int isnxt = 0;
   int isgap = 0;
   int isold = 0;
   int rc;
   int i;
   short iseq;
   short dseq;
   short nnak;
   int do_add_pkt;

   nused = nused_rslots (k);

   if (nused == 0)
   {
      eseq = rseq;              // for first time
   }
   else
   {
      high = latest_rseq (k);
      low = high - wsize + 1;
      if (low < 0)
         low += 64;

      eseq = (high + 1) & 63;

      if (rseq != eseq)
      {
         for (i = 2; i <= wsize; i++)
         {
            iseq = (high + i) & 63;
            if (rseq == iseq)
            {
               isgap = 1;
               break;
            }
         }
      }

      if (rseq != eseq && !isgap)
      {
         short hp = (high + 1) & 63;
         for (i = low; i != hp; i++)
         {
            iseq = i & 63;
            if (rseq == iseq)
            {
               isold = 1;
               break;
            }
         }
      }
   }

   if (rseq == eseq)
      isnxt = 1;
   else
      isnxt = 0;

   debug (DB_LOG, "HANDLE_RPKT rseq", 0, rseq);
   debug (DB_LOG, "  high", 0, high);
   debug (DB_LOG, "  low", 0, low);
   debug (DB_LOG, "  eseq", 0, eseq);
   debug (DB_LOG, "  nused", 0, nused);
   debug (DB_LOG, "  isnxt", 0, isnxt);
   debug (DB_LOG, "  isgap", 0, isgap);
   debug (DB_LOG, "  isold", 0, isold);

#ifdef DEBUG
   show_rslots (k);
#endif

   // write old packets to file in order to keep
   // our receive window aligned with sender's window
   if (isnxt || isgap)
   {
      debug (DB_LOG, "HANDLE_RPKT align our recv window with sender rseq",
             0, rseq);
      for (i = 0; i < wsize; i++)
      {
         iseq = earliest_rseq (k);
         debug (DB_LOG, "HANDLE_RPKT iseq", 0, iseq);
         if (iseq < 0 || iseq >= 64)
            break;

         dseq = rseq - iseq;
         if (dseq < 0)
            dseq += 64;
         debug (DB_LOG, "HANDLE_RPKT dseq", 0, dseq);
         if (dseq < wsize)
            continue;

         rslot = k->r_pw[iseq];
         debug (DB_LOG, "HANDLE_RPKT rslot", 0, rslot);
         if (rslot < 0 || rslot >= wsize)
         {
            debug (DB_LOG, "HANDLE_RPKT error 1, rslot", 0, rslot);
            debug (DB_LOG, "HANDLE_RPKT iseq", 0, iseq);
            return (X_ERROR);
         }

         if (k->ipktinfo[rslot].flg == 0)
         {
            debug (DB_MSG, "HANDLE_RPKT error 2, flg", 0,
                   k->ipktinfo[rslot].flg);
            debug (DB_LOG, "  rslot", 0, rslot);
            return (X_ERROR);
         }

         /* Decode pkt and write file */
         rc = decode (k, r, 1, k->ipktinfo[rslot].buf, rslot);
         debug (DB_LOG, "HANDLE_RPKT decode rc", 0, rc);
         if (rc != X_OK)
         {
            debug (DB_LOG, "decode failed rc", 0, rc);
#ifdef DEBUG
            show_rslots (k);
#endif
            exit (1);
            epkt ("EKSW decode failed", k);
            return (rc);
         }
         free_rslot (k, rslot);
#ifdef DEBUG
         show_rslots (k);
#endif
      }
   }

   do_add_pkt = 0;

   if (isnxt)
   {
      // 1. PSN = SEQ = HIGH + 1, the usual case
      debug (DB_LOG, "HANDLE_RPKT usual rseq", 0, rseq);

      // acknowledge incoming packet
      ack (k, rseq, (UCHAR *) 0);

      do_add_pkt = 1;
   }
   else if (isgap)
   {
      // 2. PSN != SEQ, a new packet, but not the next sequential one
      debug (DB_LOG, "HANDLE_RPKT isgap rseq", 0, rseq);

      nnak = rseq - high - 1;
      if (nnak < 0)
         nnak += 64;

      debug (DB_LOG, "  nnak", 0, nnak);

      // acknowledge incoming packet
      ack (k, rseq, (UCHAR *) 0);

      // send NAKs for whole gap: HIGH+1 to PSN-1
      for (i = 0; i < nnak; i++)
      {
         iseq = (high + 1 + i) & 63;
         rslot = k->r_pw[iseq];
         debug (DB_LOG, "HANDLE_RPKT isgap nak iseq", 0, iseq);
         debug (DB_LOG, "  rslot", 0, rslot);
         nak (k, iseq, rslot);
      }

      do_add_pkt = 2;
   }
   else if (isold)
   {
      // 3. LOW <= PSN <= HIGH, old, possibly missing pkt arrived
      debug (DB_LOG, "HANDLE_RPKT isold rseq", 0, rseq);

      ack (k, rseq, (UCHAR *) 0);

      // If packet was missing add it to window.
      // There is supposed to be a slot available.
      // Don't store a repeated packet again
      rslot = k->r_pw[rseq];
      if (rslot < 0 || rslot >= wsize)
      {
         do_add_pkt = 3;
      }
   }
   else
   {
      // 4. PSN < LOW || PSN > HIGH -- unexpected, undesired, ignore
      debug (DB_LOG, "HANDLE_RPKT unexpected so ignored rseq", 0, rseq);
   }

   if (do_add_pkt > 0)
   {
      test_rslots (k);

      get_rslot (k, &rslot);
      if (rslot < 0 || rslot >= wsize)
      {
         debug (DB_LOG, "HANDLE_RPKT error 3, rslot", 0, rslot);
         debug (DB_LOG, "  do_add_pkt", 0, do_add_pkt);
         return (X_ERROR);
      }

      test_rslots (k);

      // put pbuf into slot in receive table
      for (i = 0; i < P_BUFLEN; i++)
      {
         k->ipktinfo[rslot].buf[i] = pbuf[i];
         if (pbuf[i] == 0)
            break;
      }
      k->ipktinfo[rslot].len = i;
      k->ipktinfo[rslot].flg = 1;
      k->ipktinfo[rslot].seq = rseq;
      k->ipktinfo[rslot].crc = chk3 (pbuf, k);
      k->r_pw[rseq] = rslot;

      test_rslots (k);
   }

#ifdef DEBUG
   show_rslots (k);
#endif

   return (X_OK);
}

void
handle_bad_rpkt ()
{
}


int
ok2rxd (struct k_data *k)
{
   int ok;
   int sw_full;

   if (nused_sslots (k) == k->wslots)
      sw_full = 1;
   else
      sw_full = 0;

   if ((k->what == W_SEND || k->what == W_GET) &&
       k->state == S_DATA && sw_full == 0)
      ok = 0;
   else
      ok = 1;

   debug (DB_LOG, "ok2rxd", 0, ok);

   k->do_rxd = ok;

   return (ok);
}

STATIC void
free_sslot_easca (struct k_data *k)
// Remove earliest & subsequent contiguous ACK'd packets.
// Packet numbers thus remain in order in the sliding window.
// This way we don't send packets which won't fit in their window.
{
   int nfreed = 0;
   int i;

   for (i = 0; i < k->wslots; i++)
   {
      short seq_earl, slot_earl;
      seq_earl = earliest_sseq (k);
      if (seq_earl < 0 || seq_earl > 63)
         break;
      slot_earl = k->s_pw[seq_earl];
      if (slot_earl < 0 || slot_earl >= k->wslots)
         break;
      if (k->opktinfo[slot_earl].flg == 0)
         break;
      free_sslot (k, slot_earl);
      nfreed++;
   }
   debug (DB_LOG, "FREE_SSLOT_EASCA nfreed", 0, nfreed);
}

#ifdef DEBUG
STATIC void
chk_sseq_nos (struct k_data *k)
// check that sequence numbers are still in order
{
   int i;
   int dif, dif0;
   int ok;
   int nerr;

   nerr = 0;
   ok = 0;
   for (i = 0; i < k->wslots; i++)
   {
      short ss;
      if (k->opktinfo[i].seq < 0)
         continue;
      ss = k->opktinfo[i].seq - k->r_seq;
      if (ss < -32)
         ss += 64;
      if (ss > 31)
         ss -= 64;
      dif = ss - i;
      if (ok == 0)
      {
         ok = 1;
         dif0 = dif;
      }
      else if (dif != dif0 && (dif + k->wslots) != dif0)
         nerr++;
   }

   if (nerr)
   {
      debug (DB_LOG, "CHK_SEQ nerr", 0, nerr);
#ifdef DEBUG
      show_sslots (k);
#endif
   }
}
#endif

STATIC int
flush_to_file (struct k_data *k, struct k_response *r)
{
   short wsize = k->wslots;     // negotiated window size
   short rslot;
   short iseq;
   int rc;

   debug (DB_MSG, "FLUSH_TO_FILE begin", 0, 0);

   // flush receive window to file
   while ((iseq = earliest_rseq (k)) >= 0)
   {

      debug (DB_LOG, "FLUSH_TO_FILE iseq", 0, iseq);

      rslot = k->r_pw[iseq];

      debug (DB_LOG, "  rslot", 0, rslot);

      if (rslot < 0 || rslot >= wsize)
      {
         debug (DB_LOG, "FLUSH_TO_FILE error rslot", 0, rslot);
         return (X_ERROR);
      }

      if (k->ipktinfo[rslot].flg == 0)
      {
         debug (DB_MSG, "FLUSH_TO_FILE error flg", 0, k->ipktinfo[rslot].flg);
         return (X_ERROR);
      }

      /* Decode pkt and write file */
      rc = decode (k, r, 1, k->ipktinfo[rslot].buf, rslot);
      debug (DB_LOG, "FLUSH_TO_FILE decode rc", 0, rc);
      if (rc != X_OK)
      {
         debug (DB_LOG, "decode failed rc", 0, rc);
#ifdef DEBUG
         show_rslots (k);
#endif
         exit (1);
         epkt ("EKSW flush to file failed", k);
         return (rc);
      }
      free_rslot (k, rslot);
#ifdef DEBUG
      show_rslots (k);
#endif
   }

   debug (DB_LOG, "FLUSH_TO_FILE obufpos", 0, k->obufpos);

   if (k->obufpos > 0)
   {                            /* Flush output buffer to file */
      rc = (*(k->writef)) (k, k->obuf, k->obufpos);
      debug (DB_LOG, "FLUSH_TO_FILE writef rc", 0, rc);
      r->sofar += k->obufpos;
      r->sofar_rumor += k->obufpos;
      k->obufpos = 0;
   }

   return (rc);
}

/*
 * Utility routines 
 */

UCHAR *
get_rslot (struct k_data * k, short *n)
{                               /* Find a free packet buffer */
   register int slot;
   /*
    * 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 (slot = 0; slot < k->wslots; slot++)
   {                            /* Search */
      if (k->ipktinfo[slot].len < 1)
      {
         *n = slot;             /* Slot number */
         k->ipktinfo[slot].len = -1;    /* Mark it as allocated but not used */
         k->ipktinfo[slot].seq = -1;
         k->ipktinfo[slot].typ = SP;
         k->ipktinfo[slot].crc = 0xFFFF;
         /*
          * k->ipktinfo[slot].rtr = 0; 
          *//*
          * (see comment above) 
          */
         k->ipktinfo[slot].dat = (UCHAR *) 0;
         debug (DB_LOG, "GET_RSLOT slot", 0, slot);
         return (k->ipktinfo[slot].buf);
      }
   }
   *n = -1;
   debug (DB_LOG, "GET_RSLOT slot", 0, -1);
   return ((UCHAR *) 0);
}

void                            /* Initialize a window slot */
free_rslot (struct k_data *k, short slot)
{
   short seq;

   debug (DB_LOG, "FREE_RSLOT slot", 0, slot);

   if (slot < 0 || slot >= P_WSLOTS)
   {
      debug (DB_LOG, "  confused: slot out of bounds", 0, slot);
      epkt ("EKSW free_rslot confused", k);
      exit (1);
      return;
   }

   seq = k->ipktinfo[slot].seq;
   debug (DB_LOG, "  seq", 0, seq);

   if (seq >= 0 && seq < 64)
      k->r_pw[seq] = -1;

   k->ipktinfo[slot].len = 0;   /* Packet length */
   k->ipktinfo[slot].seq = -1;  /* Sequence number */
   k->ipktinfo[slot].typ = (char) 0;    /* Type */
   k->ipktinfo[slot].rtr = 0;   /* Retry count */
   k->ipktinfo[slot].flg = 0;   /* Flags */
   k->ipktinfo[slot].crc = 0xFFFF;
}

UCHAR *
get_sslot (struct k_data *k, short *n)
{                               /* Find a free packet buffer */
   register int slot;
   for (slot = 0; slot < k->wslots; slot++)
   {                            /* Search */
      if (k->opktinfo[slot].len < 1)
      {
         *n = slot;             /* Slot number */
         k->opktinfo[slot].len = -1;    /* Mark it as allocated but not used */
         k->opktinfo[slot].seq = -1;
         k->opktinfo[slot].typ = SP;
         k->opktinfo[slot].rtr = 0;
         k->opktinfo[slot].flg = 0;     // ACK'd bit
         k->opktinfo[slot].dat = (UCHAR *) 0;
         debug (DB_LOG, "GET_SSLOT slot", 0, slot);
         return (k->opktinfo[slot].buf);
      }
   }
   *n = -1;
   debug (DB_LOG, "GET_SSLOT slot", 0, -1);
   return ((UCHAR *) 0);
}

STATIC int
nused_sslots (struct k_data *k)
{
   int i;
   int n = 0;

   for (i = 0; i < k->wslots; i++)
      if (k->opktinfo[i].len > 0)
         n++;
   return (n);
}

STATIC int
nused_rslots (struct k_data *k)
{
   int i;
   int n = 0;

   for (i = 0; i < k->wslots; i++)
      if (k->ipktinfo[i].len > 0)
         n++;
   return (n);
}

#ifdef DEBUG
STATIC void
show_sslots (struct k_data *k)
{
   int slot, j, n;
   char tmp[80], *p;

   debug (DB_MSG, "SHOW_SSLOTS", 0, 0);
   for (slot = 0; slot < k->wslots; slot++)
   {
      n = snprintf (tmp, sizeof (tmp) - 1, "  slot=%02d seq=%02d flg=%d "
                    "len=%4d crc=%5d pwi=",
                    slot, k->opktinfo[slot].seq, k->opktinfo[slot].flg,
                    k->opktinfo[slot].len, k->opktinfo[slot].crc);

      p = tmp + n;
      for (j = 0; j < 64; j++)
         if (k->s_pw[j] == slot)
         {
            n = snprintf (p, sizeof (tmp) - 1 - n, " %02d", j);
            p += n;
         }
      debug (DB_MSG, tmp, 0, 0);
   }
}
#endif /* DEBUG */
#ifdef DEBUG
STATIC void
show_rslots (struct k_data *k)
{
   int slot, j, n, m;
   char tmp[256], *p;


   debug (DB_MSG, "SHOW_RSLOTS", 0, 0);
   for (slot = 0; slot < k->wslots; slot++)
   {
      int ok;
      if (k->ipktinfo[slot].seq >= 0 &&
          chk3 (k->ipktinfo[slot].buf, k) != k->ipktinfo[slot].crc)
         ok = 0;
      else
         ok = 1;

      p = tmp;
      m = sizeof (tmp) - 1;
      n = snprintf (p, m, "  slot=%02d seq=%02d flg=%d "
                    "len=%4d crc=%5d crcok=%d pwi=",
                    slot, k->ipktinfo[slot].seq, k->ipktinfo[slot].flg,
                    k->ipktinfo[slot].len, k->ipktinfo[slot].crc, ok);
      if (n < 0)
      {
         debug (DB_LOG, "SHOW_RSLOTS 1 n", 0, n);
         debug (DB_LOG, "SHOW_RSLOTS 1 m", 0, m);
         debug (DB_LOG, "SHOW_RSLOTS 1 slot", 0, slot);
         debug (DB_LOG, "SHOW_RSLOTS 1 seq", 0, k->ipktinfo[slot].seq);
         debug (DB_LOG, "SHOW_RSLOTS 1 flg", 0, k->ipktinfo[slot].flg);
         debug (DB_LOG, "SHOW_RSLOTS 1 len", 0, k->ipktinfo[slot].len);
         debug (DB_LOG, "SHOW_RSLOTS 1 crc", 0, k->ipktinfo[slot].crc);
         debug (DB_LOG, "SHOW_RSLOTS ok", 0, ok);
         debug (DB_LOG, "SHOW_RSLOTS 1 p-tmp", 0, p - tmp);
         debug (DB_LOG, "SHOW_RSLOTS 1 tmp", tmp, 0);
         exit (1);
      }
      p += n;
      m -= n;
      for (j = 0; j < 64; j++)
      {
         if (k->r_pw[j] == slot)
         {
            n = snprintf (p, m, " %02d", j);
            if (n < 0)
            {
               debug (DB_LOG, "SHOW_RSLOTS 2 j", 0, j);
               debug (DB_LOG, "SHOW_RSLOTS 2 n", 0, n);
               debug (DB_LOG, "SHOW_RSLOTS 2 m", 0, m);
               debug (DB_LOG, "SHOW_RSLOTS 2 slot", 0, slot);
               debug (DB_LOG, "SHOW_RSLOTS 2 seq", 0, k->ipktinfo[slot].seq);
               debug (DB_LOG, "SHOW_RSLOTS 2 flg", 0, k->ipktinfo[slot].flg);
               debug (DB_LOG, "SHOW_RSLOTS 2 len", 0, k->ipktinfo[slot].len);
               debug (DB_LOG, "SHOW_RSLOTS 2 crc", 0, k->ipktinfo[slot].crc);
               debug (DB_LOG, "SHOW_RSLOTS 2 p-tmp", 0, p - tmp);
               debug (DB_LOG, "SHOW_RSLOTS 2 tmp", tmp, 0);
               exit (1);
            }
            p += n;
            m -= n;
         }
      }
      debug (DB_MSG, tmp, 0, 0);
   }
}
#endif

STATIC int
test_rslots (struct k_data *k)
{
   int slot, nbad = 0;

   for (slot = 0; slot < k->wslots; slot++)
   {
      if (k->ipktinfo[slot].seq >= 0 &&
          chk3 (k->ipktinfo[slot].buf, k) != k->ipktinfo[slot].crc)
      {
         nbad++;
         debug (DB_HEX, "THEX", k->ipktinfo[slot].buf, k->ipktinfo[slot].len);
      }
   }
   if (nbad > 0)
   {
      debug (DB_LOG, "TEST_RSLOTS nbad", 0, nbad);
#ifdef DEBUG
      show_rslots (k);
# endif
      epkt ("EKSW test rslots failed", k);
      exit (1);
//    return X_ERROR;
   }
   return X_OK;
}

void                            /* Initialize a window slot */
free_sslot (struct k_data *k, short slot)
{
   short seq;

   debug (DB_LOG, "FREE_SSLOT slot", 0, slot);
   if (slot < 0 || slot >= P_WSLOTS)
      return;

   seq = k->opktinfo[slot].seq;
   debug (DB_LOG, "  seq", 0, seq);

   if (seq >= 0 && seq < 64)
      k->s_pw[seq] = -1;

   k->opktinfo[slot].len = 0;   /* Packet length */
   k->opktinfo[slot].seq = -1;  /* Sequence number */
   k->opktinfo[slot].typ = (char) 0;    /* Type */
   k->opktinfo[slot].rtr = 0;   /* Retry count */
   k->opktinfo[slot].flg = 0;   /* ACK'd bit */
}

STATIC short
earliest_sseq (struct k_data *k)
{
   short slot;
   short seq;
   short age;
   short oldest_seq = -1;
   short oldest_age = -99;

   for (slot = 0; slot < k->wslots; slot++)
   {
      seq = k->opktinfo[slot].seq;
      if (seq >= 0 && seq < 64)
      {
         age = k->s_seq - seq;
         if (age < 0)
            age += 64;
         if (age > 63)
            age -= 64;

         if (age > oldest_age)
         {
            oldest_age = age;
            oldest_seq = seq;
         }
      }
   }

   debug (DB_LOG, "EARLIEST_SSEQ", 0, oldest_seq);
   return (oldest_seq);
}

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

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

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

STATIC USHORT
chk2 (UCHAR * pkt, struct k_data *k)
{
   register USHORT chk;
   for (chk = 0; *pkt != '\0'; pkt++)
      chk += *pkt;
   return (chk);
}

/*
 * 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. 
 */
STATIC USHORT
chk3 (UCHAR * pkt, struct k_data * k)
{
   register USHORT c, crc;
   for (crc = 0; *pkt != '\0'; pkt++)
   {
      c = crc ^ (*pkt);
      crc = (crc >> 8) ^ ((k->crcta[(c & 0xF0) >> 4]) ^ (k->crctb[c & 0x0F]));
   }
   return (crc);
}

/*
 * 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 
 */
STATIC int
spkt (char typ, short seq, int len, UCHAR * data, struct k_data *k)
{
   int retc;

   unsigned int crc;            /* For building CRC */
   int i, j, lenpos;            /* Workers */
   UCHAR *s, *buf;
   int buflen;
   short slot;
   UCHAR tmp[100];              // for packets we don't want to resend

   debug (DB_CHR, "SPKT typ", 0, typ);
   debug (DB_LOG, "  seq", 0, seq);
   debug (DB_LOG, "  len", 0, len);

   if (seq < 0 || seq > 63)
      return (X_ERROR);

   if (len < 0)
   {                            /* Calculate data length ourselves? */
      len = 0;
      s = data;
      while (*s++)
         len++;
      debug (DB_LOG, "SPKT calc len", 0, len);
   }
   if (typ == 'Y' || typ == 'N' || typ == 'E')
   {
      buf = tmp;
      buflen = sizeof (tmp);
   }
   else
   {
      debug (DB_LOG, "SPKT k->s_seq", 0, k->s_seq);
      debug (DB_LOG, "SPKT k->s_pw[seq]", 0, k->s_pw[seq]);

      get_sslot (k, &slot);     // get a new send slot
      if (slot < 0 || slot >= k->wslots)
         return (X_ERROR);
      k->s_pw[k->s_seq] = slot;

      // save these for use in resend()
      k->opktinfo[slot].typ = typ;
      k->opktinfo[slot].seq = seq;
      k->opktinfo[slot].len = len;

      buf = k->opktinfo[slot].buf;
      buflen = P_BUFLEN;
   }

   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 */
	if(k->bcta3)
		k->bct = 3;
   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], k));       /* 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++)
      {
         if (i < 0 || i >= buflen)
         {
            debug (DB_LOG, "confused copy data i", 0, i);
            epkt ("EKSW spkt confused", k);
            exit (1);
            return (X_ERROR);
         }
         buf[i] = *data++;
      }
   buf[i] = '\0';

   switch (k->bct)
   {                            /* Add block check */
   case 1:                     /* 1 = 6-bit chksum */
      buf[i++] = tochar (chk1 (&buf[lenpos], k));
      break;
   case 2:                     /* 2 = 12-bit chksum */
      j = chk2 (&buf[lenpos], k);
#if 0
      buf[i++] = (unsigned) tochar ((j >> 6) & 077);
      buf[i++] = (unsigned) tochar (j & 077);
#else
      // HiTech's XAC compiler silently ruins the above code.
      // An intermediate variable provides a work-around.
      // 2004-06-29 -- JHD
      {
         USHORT jj;
         jj = (j >> 6) & 077;
         buf[i++] = tochar (jj);
         jj = j & 077;
         buf[i++] = tochar (jj);
      }
#endif

      break;
   case 3:                     /* 3 = 16-bit CRC */
      crc = chk3 (&buf[lenpos], k);
#if 0
      buf[i++] = (unsigned) tochar (((crc & 0170000)) >> 12);
      buf[i++] = (unsigned) tochar ((crc >> 6) & 077);
      buf[i++] = (unsigned) tochar (crc & 077);
#else
      // HiTech's XAC compiler silently ruins the above code.
      // An intermediate variable provides a work-around.
      // 2004-06-29 -- JHD
      {
         USHORT jj;
         jj = (crc >> 12) & 0x0f;
         buf[i++] = tochar (jj);
         jj = (crc >> 6) & 0x3f;
         buf[i++] = tochar (jj);
         jj = crc & 0x3f;
         buf[i++] = tochar (jj);
      }
#endif
      break;
   }

   buf[i++] = k->s_eom;         /* Packet terminator */
   buf[i] = '\0';               /* String terminator */
   // packet ready to go

   if (i < 0 || i >= buflen)
   {
      debug (DB_LOG, "SPKT i", 0, i);
      debug (DB_LOG, "SPKT buflen", 0, buflen);
      return (X_ERROR);
   }

   k->s_seq = seq;              /* Remember sequence number */
   k->opktlen = i;              /* Remember length for retransmit */

   if (typ != 'Y' && typ != 'N' && typ != 'E')
   {
      k->opktinfo[slot].len = i;        /* Remember length for retransmit */
   }

   debug (DB_LOG, "SPKT opktlen", 0, k->opktlen);

#ifdef DEBUG
   /*
    * CORRUPT THE PACKET SENT BUT NOT THE ONE WE SAVE 
    */
   if (xerror ())
   {
      UCHAR p[P_BUFLEN];
      int i;
      for (i = 0; i < buflen - 8; i++)
         if (!(p[i] = buf[i]))
            break;
      if (xerror ())
      {
         p[i - 2] = 'X';
         debug (DB_PKT, "XPKT", (char *) &p[1], 0);
      }
      else if (xerror ())
      {
         p[k->opktlen - 1] = 'N';
         debug (DB_PKT, "NPKT", (char *) &p[1], 0);
      }
      else
      {
         p[0] = 'A';
         debug (DB_PKT, "APKT", (char *) &p[1], 0);
      }
      return ((*(k->txd)) (k, p, k->opktlen));  /* Send it. */
   }
#endif /* DEBUG */

   debug (DB_PKT, "SPKT", &buf[1], k->opktlen);
   retc = ((*(k->txd)) (k, buf, k->opktlen));   /* Send buf == whole packet */
   debug (DB_LOG, "SPKT txd retc", 0, retc);

   return (retc);
}

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

STATIC int
nak (struct k_data *k, short seq, short slot)
{
   int rc;
   debug (DB_LOG, "NAK seq", 0, seq);
   debug (DB_LOG, "  slot", 0, slot);
// k->anseq = seq;
   rc = spkt ('N', seq, 0, (UCHAR *) 0, k);
   if (slot >= 0 && slot < 64 && k->ipktinfo[slot].rtr++ > k->retry)
   {
      debug (DB_MSG, "X_ERROR returned from nak(): too many retries", 0, 0);
      debug (DB_MSG, "  seq", 0, seq);
      debug (DB_MSG, "  slot", 0, slot);
      rc = X_ERROR;
   }
   return (rc);
}

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

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

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

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

   debug (DB_MSG, "SPAR", 0, 0);

   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)
      {                         // they will do 8th bit prefixing if requested
         k->ebqflg = 1;
         k->ebq = '&';
      }
      else if (s[7] == 'N')
      {                         // they refuse to do 8th bit prefixing
         /*
          * WHAT? 
          */
      }
   }
   if (datalen >= 8)
   {                            /* Block check */
		k->bct = s[8] - '0';
		if ((k->bct < 1) || (k->bct > 3))
			k->bct = 1;
		if(k->bcta3)
			k->bct = 3;
   }
   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]);

      if (!(x & CAP_LP))
         k->capas &= ~CAP_LP;

      if (!(x & CAP_SW))
         k->capas &= ~CAP_SW;

      if (!(x & CAP_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++);
   }
   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;
      }
   }

   debug (DB_LOG, "  s_maxlen", 0, k->s_maxlen);

   if (k->capas & CAP_SW)
   {
      if (datalen > y)
      {
         x = xunchar (s[y + 1]);
         k->wslots = (x > k->wslots_max) ? k->wslots_max : x;
         if (k->wslots < 1)     /* Watch out for bad negotiation */
            k->wslots = 1;
         if (k->wslots > 1)
            if (k->wslots > k->retry)   /* Retry limit must be greater */
               k->retry = k->wslots + 1;        /* than window size. */
      }
   }

   debug (DB_LOG, "  k->capas & CAP_LP", 0, k->capas & CAP_LP);
   debug (DB_LOG, "  k->capas & CAP_SW", 0, k->capas & CAP_SW);
   debug (DB_LOG, "  k->capas & CAP_AT", 0, k->capas & CAP_AT);
   debug (DB_LOG, "  k->capas & CAP_RS", 0, k->capas & CAP_RS);
   debug (DB_LOG, "  k->capas & CAP_LS", 0, k->capas & CAP_LS);
   debug (DB_CHR, "  k->ebq           ", 0, k->ebq);
   debug (DB_LOG, "  k->ebqflg        ", 0, k->ebqflg);
   debug (DB_LOG, "  k->parity        ", 0, k->parity);
   debug (DB_LOG, "  k->s_eom         ", 0, k->s_eom);
   debug (DB_LOG, "  k->r_timo        ", 0, k->r_timo);
   debug (DB_LOG, "  k->s_timo        ", 0, k->s_timo);
   debug (DB_CHR, "  k->r_ctlq        ", 0, k->r_ctlq);
   debug (DB_CHR, "  k->s_ctlq        ", 0, k->s_ctlq);
   debug (DB_CHR, "  k->rptq          ", 0, k->rptq);
   debug (DB_LOG, "  k->rptflg        ", 0, k->rptflg);
   debug (DB_LOG, "  k->bct           ", 0, k->bct);
   debug (DB_LOG, "  k->bcta3         ", 0, k->bcta3);
   debug (DB_LOG, "  k->r_maxlen      ", 0, k->r_maxlen);
   debug (DB_LOG, "  k->s_maxlen      ", 0, k->s_maxlen);
   debug (DB_LOG, "  k->wslots        ", 0, k->wslots);
   debug (DB_LOG, "  k->binary        ", 0, k->binary);
   debug (DB_LOG, "  k->retry         ", 0, k->retry);
}

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

STATIC int
rpar (struct k_data *k, char type)
{
   UCHAR *d;
   int rc, len;
   short bctsv;
   UCHAR *buf;
   short s_slot;

   debug (DB_LOG, "RPAR capas", 0, k->capas);
   debug (DB_LOG, "  wslots", 0, k->wslots);

   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;
	if(k->bcta3)
		d[7] = '3';
	else
		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->wslots);  /* Window size */

   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;

	if(k->bcta3){
		bctsv = 3;
		k->bct = 3;
	}
	else{
		bctsv = 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 */
      buf = get_sslot (k, &s_slot);     // get a new send slot
      k->s_pw[k->s_seq] = s_slot;
      debug (DB_LOG, "sending S pkt s_seq", 0, k->s_seq);
      debug (DB_LOG, "  s_slot", 0, s_slot);
      rc = spkt ('S', 0, len, d, k);
      break;
   default:
      rc = -1;
   }
   k->bct = bctsv;
   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 
 */
STATIC int
decode (struct k_data *k, struct k_response *r, short f, UCHAR * inbuf,
        int rslot)
{

   register unsigned int a, a7; /* Current character */
   unsigned int b8;             /* 8th bit */
   int rpt;                     /* Repeat count */
   int rc;                      /* Return code */
   UCHAR *ucp = 0;
   int i;
   int nobuf;
   USHORT crc;

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

   debug (DB_LOG, "DECODE rslot", 0, rslot);
   if (rslot >= 0)
   {
      k->anseq = k->ipktinfo[rslot].seq;
      debug (DB_LOG, "DECODE seq", 0, k->ipktinfo[rslot].seq);
      debug (DB_LOG, "DECODE len", 0, k->ipktinfo[rslot].len);
      debug (DB_LOG, "DECODE crc", 0, k->ipktinfo[rslot].crc);
      debug (DB_HEX, "IHEX", inbuf, k->ipktinfo[rslot].len);

      for (i = 0; i < k->ipktinfo[rslot].len; i++)
         if (inbuf[i] == 0)
            break;

      if (i != k->ipktinfo[rslot].len)
      {
         debug (DB_LOG, "DECODE error i", 0, i);
#ifdef DEBUG
         show_rslots (k);
#endif
         epkt ("EKSW decode len bad", k);
         exit (1);
         return (X_ERROR);
      }

      crc = chk3 (inbuf, k);
      if (crc != k->ipktinfo[rslot].crc)
      {
         debug (DB_LOG, "DECODE error crc", 0, crc);
#ifdef DEBUG
         show_rslots (k);
#endif
         epkt ("EKSW decode crc bad", k);
         exit (1);
         return (X_ERROR);
      }
   }

   nobuf = 0;
   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 */

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

      for (; rpt > 0; rpt--)
      {                         /* Output the char 'rpt' times */
         if (f == 0)
         {
            *ucp++ = (UCHAR) a; /* to memory */
         }
         else
         {                      /* or to file */
            k->obuf[k->obufpos++] = (UCHAR) a;  /* Deposit the byte */
            nobuf++;
            if (k->obufpos == k->obuflen)
            {                   /* Buffer full? */
               rc = (*(k->writef)) (k, k->obuf, k->obuflen);    /* Dump it. */
               debug (DB_LOG, "DECODE writef rc", 0, rc);
               r->sofar += k->obuflen;
               r->sofar_rumor += k->obuflen;
               if (rc != X_OK)
                  break;
               k->obufpos = 0;
            }
         }
      }
   }

   debug (DB_LOG, "DECODE nobuf", 0, nobuf);
   debug (DB_LOG, "DECODE obufpos", 0, k->obufpos);

   if (f == 0)                  /* If writing to memory */
      *ucp = '\0';              /* terminate the string */

   debug (DB_LOG, "DECODE rc", 0, rc);
   return (rc);
}

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

STATIC UCHAR *                  /* Convert number to string */
numstring (ULONG n, UCHAR * buf, int buflen, struct k_data * k)
{
   int i, x;
   buf[buflen - 1] = '\0';
   for (i = buflen - 2; i > 0; i--)
   {
      x = n % 10L;
      buf[i] = x + '0';
      n /= 10L;
      if (!n)
         break;
   }
   if (n)
   {
      return ((UCHAR *) 0);
   }
   if (i > 0)
   {
      UCHAR *p, *s;
      s = &buf[i];
      p = buf;
      while ((*p++ = *s++));
      *(p - 1) = '\0';
   }
   return ((UCHAR *) buf);
}

/*
 * G A T T R -- Read incoming attributes.
 * 
 * Returns: -1 if no transfer mode (text/binary) was announced. 0 if text
 * was announced. 1 if binary was announced. 
 */

#define SIZEBUFL 32             /* For number conversions */

STATIC int
gattr (struct k_data *k, UCHAR * s, struct k_response *r)
{
   long fsize = 0, fsizek = 0;  /* File size */
   UCHAR c;                     /* Workers */
   int aln, i, rc;

   UCHAR sizebuf[SIZEBUFL];

   rc = -1;
   while ((c = *s++))
   {                            /* Get attribute tag */
      aln = xunchar (*s++);     /* Length of attribute string */
      switch (c)
      {
      case '!':                /* File length in K */
      case '"':                /* File type */
         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 */
         if (c == '!')
         {                      /* Length */
            fsizek = stringnum (sizebuf, k);    /* Convert to number */
         }
         else
         {                      /* Type */
            if (sizebuf[0] == 'A')      /* Text */
               rc = 0;
            else if (sizebuf[0] == 'B') /* Binary */
               rc = 1;
            debug (DB_LOG, "GATTR rc", 0, rc);
            debug (DB_LOG, "GATTR sizebuf", sizebuf, 0);
         }
         break;

      case '#':                /* File creation date */
         for (i = 0; (i < aln) && (i < DATE_MAX); i++)
            r->filedate[i] = *s++;      /* save it to a 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, k);        /* Convert to number */
         break;

      default:                 /* Unknown attribute */
         s += aln;              /* Just skip past it */
         break;
      }
   }
   if (fsize > -1L)
   {                            /* Remember the file size */
      r->filesize = fsize;
   }
   else if (fsizek > -1L)
   {
      r->filesize = fsizek * 1024L;
   }
   debug (DB_LOG, "gattr r->filesize", 0, (r->filesize));
   debug (DB_LOG, "gattr r->filedate=", r->filedate, 0);
   return (rc);
}

#define ATTRLEN 48

STATIC int
sattr (struct k_data *k, struct k_response *r)
{                               /* Build and send A packet */
   // int i, x, aln;
   int i, x;
   short tmp;
   long filelength;
   UCHAR datebuf[DATE_MAX], *p;
   UCHAR *buf;
   short s_slot;

   debug (DB_PKT, "SATTR k->zincnt 0", 0, (k->zincnt));

   tmp = k->binary;
   filelength = (*(k->finfo))
      (k, k->filename, datebuf, DATE_MAX, &tmp, k->xfermode);
   k->binary = tmp;

   debug (DB_LOG, "  filename: ", k->filename, 0);
   debug (DB_LOG, "  filedate: ", datebuf, 0);
   debug (DB_LOG, "  filelength", 0, filelength);
   debug (DB_LOG, "  binary", 0, (k->binary));

   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];
      r->filesize = filelength;
      p = numstring (filelength, lenbuf, 16, k);
      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++;
         }
      }
   }
   debug (DB_LOG, "SATTR datebuf: ", datebuf, 0);

   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++;
         /*
          * Also copy modtime to result struct 
          */
         for (x = 0; x < DATE_MAX - 1 && datebuf[x]; x++)
            r->filedate[x] = datebuf[x];
         r->filedate[x] = '\0';
      }
   }
   k->xdata[i++] = '@';         /* End of Attributes */
   k->xdata[i++] = ' ';
   k->xdata[i] = '\0';          /* Terminate attribute string */
   debug (DB_PKT, "SATTR k->xdata: ", k->xdata, 0);
   buf = get_sslot (k, &s_slot);        // get a new send slot
   k->s_pw[k->s_seq] = s_slot;
   debug (DB_LOG, "SATTR sending A pkt s_seq", 0, k->s_seq);
   debug (DB_LOG, "  s_slot", 0, s_slot);
   return (spkt ('A', k->s_seq, -1, k->xdata, k));
}

STATIC int
getpkt (struct k_data *k, struct k_response *r)
{                               /* Fill a packet from file */
   int i, next, rpt, maxlen;
// static int c;                /* PUT THIS IN STRUCT */
   int c = k->cgetpkt;

   debug (DB_LOG, "GETPKT k->s_first", 0, k->s_first);
   debug (DB_PKT, "  k->s_remain=", k->s_remain, 0);

	if(k->bcta3)
		k->bct = 3;
   maxlen = k->s_maxlen - k->bct - 3 - 6;   /* Maximum data length */
   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... */
#ifdef USE_ZGETC_MACRO
         c = zgetc ();
#else
         c = zgetc (k);
#endif
      }

      k->cgetpkt = c;

      if (c < 0)
      {                         /* Watch out for empty file. */
         debug (DB_LOG, "GETPKT first c", 0, c);
         k->s_first = -1;
         return (k->size = 0);
      }
      // r->sofar++; // makes it too big
      if (k->state == S_DATA)
         r->sofar_rumor++;
      debug (DB_LOG, "GETPKT first state", 0, k->state);
      debug (DB_CHR, "  first c", 0, c);
   }
   else if (k->s_first == -1 && !k->s_remain[0])
   {                            /* EOF from last time? */
      return (k->size = 0);
   }

   // string copy s_remain to xdata
   for (k->size = 0;
        (k->xdata[k->size] = k->s_remain[k->size]) != '\0'; (k->size)++);

   // set s_remain to zero length
   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
      {
#ifdef USE_ZGETC_MACRO
         next = zgetc ();
#else
         next = zgetc (k);
#endif
      }
      if (next < 0)
      {                         /* If none, we're at EOF. */
         k->s_first = -1;
      }
      else
      {                         /* Otherwise */
         r->sofar_rumor++;      /* count this byte */
      }
      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. */

      k->cgetpkt = c;

      if (k->size == maxlen)
      {                         /* Just at end, done. */
         debug (DB_LOG, "GETPKT size perfect c", 0, c);
         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++);
         debug (DB_LOG, "GETPKT size past end i", 0, i);
         k->size = k->osize;
         k->xdata[k->size] = '\0';
         return (k->size);      /* Return size. */
      }
   }
   if (k->size > maxlen)
   {
      debug (DB_LOG, "GETPKT error confused: from getpkt() k->size",
             0, k->size);
      epkt ("EKSW getpkt confused", k);
      exit (1);
      return (X_ERROR);
   }
   return (k->size);            /* EOF, return size. */
}

STATIC int
sdata (struct k_data *k, struct k_response *r)
{                               /* Send a data packet */
   int len, rc;
   if (k->cancel)
   {                            /* Interrupted */
      debug (DB_LOG, "SDATA interrupted k->cancel", 0, (k->cancel));
      return (0);
   }
   len = getpkt (k, r);         /* Fill data field from input file */
   debug (DB_LOG, "SDATA getpkt len", 0, len);
   if (len < 1)
   {
      debug (DB_LOG, "SDATA getpkt got eof s_seq", 0, k->s_seq);
      return (0);
   }
   debug (DB_LOG, "SDATA sending D pkt s_seq", 0, k->s_seq);

   rc = spkt ('D', k->s_seq, len, k->xdata, k); /* Send the packet */

   debug (DB_LOG, "SDATA spkt rc", 0, rc);
   return ((rc == X_ERROR) ? rc : len);
}

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

STATIC void
epkt (char *msg, struct k_data *k)
{
   int bctsv;

	if(k->bcta3){
		bctsv = 3;
		k->bct = 3;
	}
	else{
		bctsv = k->bct;
		k->bct = 1;
	}
   debug (DB_LOG, "EPKT msg:", msg, 0);
   spkt ('E', 0, -1, (UCHAR *) msg, k);
   k->bct = bctsv;
}

STATIC int                      /* Fill a packet from string s. */
encstr (UCHAR * s, struct k_data *k, struct k_response *r)
{
   k->s_first = 1;              /* Start lookahead. */
   k->istring = s;              /* Set input string pointer */
   getpkt (k, r);               /* 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 
 */

#if 0                           // never used
STATIC 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, -1);
   *(k->ostring) = '\0';        /* Terminate with null */
   k->ostring = (UCHAR *) 0;    /* Reset output string pointer */
}
#endif

STATIC void
encode (int a, int next, struct k_data *k)
{                               /* Encode character into packet == k->xdata */
   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 -- conservative */
// if (a7==0 || a7==1 || a7==13)  // 2004-07-04 -- JHD -- need 127 for telnet
	// this is a bit more conservative than C-Kermit "set prefixing minimal" 
   if (a7 == 0 || a7 == 1 || a7==3 || a7==4 || a7==10 ||
		a7 == 13 || a7==21 || a7 == 127) 
   {
      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. */

   if (k->size < 0 || k->size >= P_PKTLEN + 2)
   {
      debug (DB_LOG, "confused: from encode() k->size", 0, k->size);
      epkt ("EKSW encode confused", k);
      exit (1);
      return;
   }
}

STATIC short
nxtpkt (struct k_data *k)
{                               /* Get next packet to send */
   k->s_seq = (k->s_seq + 1) & 63;      /* Next sequence number */
   k->s_cnt++;
   k->xdata = k->xdatabuf;
   return (k->s_seq);
}

STATIC int
resend (struct k_data *k, short seq)
{
   UCHAR *buf;
   int ret;
   short slot;

   debug (DB_LOG, "RESEND seq", 0, seq);

   if (seq < 0)
      seq = earliest_sseq (k);
   if (seq < 0)
   {
      debug (DB_LOG, "RESEND failed: no earliest seq", 0, seq);
#ifdef DEBUG
      show_sslots (k);
#endif
      return (X_OK);
   }
   slot = k->s_pw[seq];
   if (slot < 0)
   {
      debug (DB_LOG, "RESEND failed: no slot for seq", 0, seq);
#ifdef DEBUG
      show_sslots (k);
#endif
      seq = earliest_sseq (k);
      if (seq < 0)
      {
         debug (DB_LOG, "RESEND failed: still no earliest seq", 0, seq);
#ifdef DEBUG
         show_sslots (k);
#endif
         return (X_OK);
      }
      slot = k->s_pw[seq];
      debug (DB_LOG, "RESEND sending earliest seq", 0, seq);
      debug (DB_LOG, "  slot", 0, slot);
   }
   if (slot < 0)
   {
      debug (DB_LOG, "RESEND failed: still bad slot", 0, slot);
#ifdef DEBUG
      show_sslots (k);
#endif
      return (X_OK);
   }

   debug (DB_MSG, "RESEND opktinfo:", 0, 0);
   debug (DB_LOG, "  seq", 0, k->opktinfo[slot].seq);
   debug (DB_CHR, "  typ", 0, k->opktinfo[slot].typ);
   debug (DB_LOG, "  len", 0, k->opktinfo[slot].len);
   debug (DB_LOG, "  rtr", 0, k->opktinfo[slot].rtr);
   debug (DB_LOG, "  crc", 0, k->opktinfo[slot].crc);
   debug (DB_LOG, "  flg", 0, k->opktinfo[slot].flg);

   k->opktlen = k->opktinfo[slot].len;

   if (k->opktlen < 0 || k->opktlen >= P_BUFLEN)
   {
      debug (DB_LOG, "RESEND error opktlen", 0, k->opktlen);
      return (X_ERROR);
   }

   // what about .seq, .typ, .rtr, .flg ?

   if (!k->opktlen)             /* Nothing to resend */
      return (X_OK);

   buf = k->opktinfo[slot].buf;

   k->opktinfo[slot].rtr++;
   if (k->opktinfo[slot].rtr > k->retry)
   {
      debug (DB_LOG, "RESEND error retries", 0, k->opktinfo[slot].rtr);
      return (X_ERROR);
   }

   debug (DB_PKT, ">PKT", &buf[1], k->opktlen);
   ret = ((*(k->txd)) (k, buf, k->opktlen));
   debug (DB_LOG, "RESEND txd ret", 0, ret);
   return (ret);
}

int                             /* The kermit() function */
kermit (short fc,               /* Function code */
        struct k_data *k,       /* The control struct */
        int len,                /* Length of packet in buf */
        char *msg,              /* Message for error packet */
        struct k_response *r)   /* Response struct */
{
   int did_a_pkt = 0;
   UCHAR *buf = 0;
   short s_slot;

   // int i, j, rc; /* Workers */
   int i, rc;                   /* Workers */
   int datalen = 0;             /* Length of packet data field */
   // int bctu; /* Block check type for this packet */
   UCHAR *pdf = 0;              /* Pointer to packet data field */
   UCHAR *qdf = 0;              /* Pointer to data to be checked */
   UCHAR *s = 0;                /* Worker string pointer */
   // UCHAR c, t; /* Worker chars */
   UCHAR rtyp = 0;              /* Worker chars */
   UCHAR c;                     /* Worker chars */
   UCHAR pbc[4];                /* Copy of packet block check */
   short rseq = 0;              // actual received packet number
   short rlen = 0;
   short chklen;                /* Length of packet block check */
   unsigned int crc;            /* 16-bit CRC */
   int ok;

   /* Mark each entry: */
   debug (DB_LOG, "KERMIT ---------------------- version", VERSION, 0);
   debug (DB_LOG, "  fc", 0, fc);
   debug (DB_LOG, "  state", 0, k->state);
   debug (DB_LOG, "  zincnt", 0, (k->zincnt));
   debug (DB_LOG, "  k->wslots", 0, k->wslots);

   test_rslots (k);

#ifdef DEBUG
   show_sslots (k);
#endif

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

      k->version = (UCHAR *) VERSION;   /* Version of this module */
      r->filename[0] = '\0';    /* No filename yet. */
      r->filedate[0] = '\0';    /* No filedate yet. */
      r->filesize = 0L;         /* No filesize yet. */
      r->sofar = 0L;            /* No bytes transferred yet */
      r->sofar_rumor = 0L;      /* No bytes transferred yet */


      for (i = 0; i < P_WSLOTS; i++)
      {                         /* Packet info for each window slot */
         free_rslot (k, i);
         free_sslot (k, i);
      }
      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" */
      }

/* Initialize the k_data structure */

      k->sw_full = 0;
      k->do_rxd = 1;
      k->s_cnt = 0;

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

      k->state = R_WAIT;        /* Beginning protocol state */
      r->rstatus = R_WAIT;
      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_cnt = 0;             /* Packet count */
      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 = k->p_maxlen;        /* Maximum packet length */
      k->s_maxlen = k->p_maxlen;        /* Maximum packet length */
      k->wslots = k->wslots_max;        /* Current window slots */
      k->zincnt = 0;
      k->filename = (UCHAR *) 0;

      /* 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 */
         | CAP_LP               /* Long packets */
         | CAP_SW               /* Sliding windows */
         | CAP_AT               /* Attribute packets */
         ;

      for (i = 0; i < P_WSLOTS; i++)
      {
         k->ipktinfo[i].buf = k->ipktbufs + i * P_BUFLEN;
         k->ipktinfo[i].buf[0] = '\0';
         k->ipktinfo[i].len = 0;
         k->ipktinfo[i].seq = -1;
         k->ipktinfo[i].typ = SP;
         k->ipktinfo[i].dat = (UCHAR *) (0);
         k->ipktinfo[i].crc = 0xFFFF;
      }

      for (i = 0; i < P_WSLOTS; i++)
      {
         k->opktinfo[i].buf = k->opktbuf + i * P_BUFLEN;
         k->opktinfo[i].buf[0] = '\0';
         k->opktinfo[i].len = 0;
         k->opktinfo[i].seq = -1;
         k->opktinfo[i].typ = SP;
         k->opktinfo[i].dat = (UCHAR *) (0);
      }

      k->opktlen = 0;

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

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

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

      return (X_OK);

   }
   else if (fc == K_GET)
   {
      /*
       * Send R packet with filenames we want.
       * Filenames cannot have spaces
       * since names are separated by spaces.
       */
      int i;
      char *p, *q;

      debug (DB_LOG, "function code == K_GET fc", 0, fc);

      p = (char *) (k->obuf);
      for (i = 0;; i++)
      {
         q = (char *) (k->filelist[i]);
         if (q == 0)
            break;
         if (i > 0)
            *p++ = ' ';
         while (*q)
            *p++ = *q++;
      }
      *p = 0;

      debug (DB_LOG, "before encstr k->obuf", k->obuf, 0);
      k->xdata = k->xdatabuf;
      encstr (k->obuf, k, r);   // Encode the name for transmission 
      debug (DB_LOG, "after encstr k->xdata", k->xdata, 0);

      buf = get_sslot (k, &s_slot);     // get a new send slot
      k->s_pw[k->s_seq] = s_slot;
		if(k->bcta3)
			k->bct = 3;
		else
			k->bct = 1;
      debug (DB_LOG, "K_GET sending R pkt s_seq", 0, k->s_seq);
      if (spkt ('R', k->s_seq, -1, k->xdata, k) != X_OK)
      {
         debug (DB_LOG, "K_GET error spkt(R) failed fc", 0, fc);
         return (X_ERROR);      /* I/O error, quit. */
      }
      k->state = R_WAIT;        /* All OK, switch states */
      r->rstatus = R_WAIT;
      k->what = W_GET;
      return (X_OK);
   }
   else if (fc == K_SEND)
   {
      if (rpar (k, 'S') != X_OK)        /* Send S packet with my parameters */
      {
         debug (DB_LOG, "K_SEND error rpar(S) failed fc", 0, fc);
         return (X_ERROR);      /* I/O error, quit. */
      }
      k->state = S_INIT;        /* All OK, switch states */
      r->rstatus = S_INIT;
      k->what = W_SEND;         /* Act like a sender */
      return (X_OK);
   }
   else if (fc == K_STATUS)
   {                            /* Status report requested. */
      debug (DB_LOG, "function code == K_STATUS fc", 0, fc);
      return (X_STATUS);        /* File name, date, size, if any. */
   }
   else if (fc == K_QUIT)
   {                            /* You told me to quit */
      debug (DB_LOG, "function code == K_QUIT fc", 0, fc);
      return (X_DONE);          /* so I quit. */
   }
   else if (fc == K_SYNC)
   {
      debug (DB_LOG, "function code == K_SYNC fc", 0, fc);
      epkt (msg, k);
      return (X_OK);
   }
   else if (fc == K_ERROR)
   {                            /* Send an error packet... */
      debug (DB_LOG, "K_ERROR error fc", 0, fc);
      epkt (msg, k);
      k->closef (k, 0, (k->state == S_DATA) ? 1 : 2);   /* Close file */
      return (X_DONE);          /* and quit. */
   }
   else if (fc != K_RUN)
   {                            /* Anything else is an error. */
      debug (DB_LOG, "not K_RUN error fc", 0, fc);
      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 */
   debug (DB_MSG, "In the protocol", 0, 0);

   if (k->do_rxd)
   {                            // we tried to read a packet in main loop

      debug (DB_LOG, "DO_RXD len", 0, len);     // in kermit call list

      if (len < 4)
      {                         /* Packet obviously no good? */
         int ret;

         if (k->what == W_RECV) /* If receiving */
         {
            debug (DB_MSG, "DO_RXD len<4", 0, 0);
#ifdef USE_NAK_OLDEST_UNACKED
            nak_oldest_unacked (k, -1);
#endif
				if(k->state == R_FILE)
				{
					debug (DB_MSG, "calling rpar again", 0, 0);
					rc = rpar (k, 'Y');    /* ACK again with my parameters */
				}
            return (X_OK);
         }
         else                   /* If W_SEND or W_GET */
         {
            debug (DB_MSG, "DO_RXD len<4: resending earliest", 0, 0);
            ret = resend (k, -1);       /* retransmit earliest packet in queue. */
            return (ret);
         }
      }

      /* Parse the packet */

      if (k->what == W_RECV)
      {                         /* If we're sending ACKs */
         switch (k->cancel)
         {                      /* Get cancellation code if any */
         case 0:
            s = (UCHAR *) 0;
            break;
         case 1:
            s = (UCHAR *) "X";
            break;
         case 2:
            s = (UCHAR *) "Z";
            break;
         }
      }

      pdf = k->ipktbuf;

      qdf = pdf;                /* Pointer to data to be checked */
      rlen = xunchar (*pdf++);  /* Length field */
      rseq = xunchar (*pdf++);  /* Received Sequence number */
      rtyp = *pdf++;            /* Type */

		if(k->state == S_EOT &&
		   len==4 && rseq==0 && rtyp=='N' && *pdf==0x33)
		{
         debug (DB_MSG, "Got NAK for seq=0 after sending B-pkt", 0, 0);
         debug (DB_MSG, "  so assuming we are done.", 0, 0);
			return (X_DONE);          /* (or X_ERROR) */
		}

      // Really to use rseq the packet must be validated by CRC first.
      if (rseq < 0 || rseq > 63)
      {
         debug (DB_LOG, "WARNING: rseq", 0, rseq);
         return (X_OK);
      }

      if ((k->what == W_RECV) && (rtyp == 'N' || rtyp == 'Y'))
      {
         /* Echo (it happens), ignore */
         return (X_OK);
      }

      if (rlen == 0)
      {                         /* Length 0 means long packet */
         c = pdf[2];            /* Get header checksum */
         pdf[2] = '\0';
         if (xunchar (c) != chk1 (pdf - 3, k))
         {                      /* Check it */
            int ret;
            debug (DB_MSG, "long pkt chk1 bad", 0, 0);
            if (k->what == W_RECV)
            {
#ifdef USE_NAK_OLDEST_UNACKED
               debug (DB_MSG, "sending NAK for oldest unacked", 0, 0);
               nak_oldest_unacked (k, -1);      /* Send NAK */
#endif
               return (X_OK);
            }
            else
            {
               debug (DB_MSG, "resending earliest", 0, 0);
               ret = resend (k, -1);
               debug (DB_LOG, "resend ret", 0, ret);
               return (ret);
            }
         }
         debug (DB_MSG, "HDR CHKSUM OK", 0, 0);
         pdf[2] = c;            /* Put checksum back */
			if(k->bcta3)
				k->bct = 3;
         datalen = xunchar (pdf[0]) * 95 + xunchar (pdf[1]) - k->bct;   /* Data length */

         debug (DB_LOG, "  long packet datalen", 0, datalen);
         debug (DB_LOG, "  rlen", 0, rlen);
         debug (DB_LOG, "  rseq", 0, rseq);

         pdf += 3;              /* Fix data pointer */
      }
      else
      {                         /* Regular packet */
			if(k->bcta3)
				k->bct = 3;
         datalen = rlen - k->bct - 2;   /* Data length */
         debug (DB_LOG, "regular packet datalen", 0, datalen);
      }


      if (rtyp == 'S' || k->state == S_INIT)
      {                         /* S-packet was
                                 * retransmitted? */
			if(k->bcta3){
				chklen = 3;
				datalen = rlen - 5;
			}
			else{
				chklen = 1;            /* Block check is always type 1 */
				datalen = rlen - 3;
			}
         debug (DB_LOG, "S-packet datalen", 0, datalen);
      }
      else
      {
			if(k->bcta3)
				k->bct = 3;
         chklen = k->bct;
      }

      debug (DB_LOG, "DO_RXD state", 0, k->state);
		debug (DB_MSG, "  These are unverified: (before blk chk tested)",0,0);
      debug (DB_LOG, "  rlen", 0, rlen);
      debug (DB_LOG, "  rseq", 0, rseq);
      debug (DB_CHR, "  rtyp", 0, rtyp);
      debug (DB_LOG, "  bct", 0, k->bct);
      debug (DB_LOG, "  bcta3", 0, k->bcta3);
      debug (DB_LOG, "  datalen", 0, datalen);
      debug (DB_LOG, "  chklen", 0, chklen);

      if (datalen < 0 || datalen + chklen + 1 >= P_BUFLEN)
      {
         debug (DB_MSG, "DO_RXD datalen out of bounds", 0, 0);
         if (k->what == W_RECV)
         {
#ifdef USE_NAK_OLDEST_UNACKED
            debug (DB_LOG, "  NAK oldest or rseq", 0, rseq);
            nak_oldest_unacked (k, rseq);
#endif
         }
         else
         {
            debug (DB_MSG, "  resend earliest", 0, 0);
            resend (k, -1);
         }
         return (X_OK);
      }

      for (i = 0; i < chklen; i++)      /* Copy the block check */
         pbc[i] = pdf[datalen + i];
      pbc[i] = '\0';            /* Null-terminate block check string */
      pdf[datalen] = '\0';      /* and the packet DATA field. */
      switch (chklen)
      {                         /* Check the block check */
      case 1:                  /* Type 1, 6-bit checksum */
         ok = (xunchar (*pbc) == chk1 (qdf, k));
#ifdef DEBUG
         if (ok && xerror ())
            ok = 0;
#endif /* DEBUG */
         if (!ok)
         {
            debug (DB_CHR, "6-bit checksum ERROR rtyp", 0, rtyp);
            debug (DB_LOG, "  rseq", 0, rseq);
            debug (DB_PKT, "  k->ipktbuf", k->ipktbuf, 0);
            if (k->what == W_RECV)
            {
#ifdef USE_NAK_OLDEST_UNACKED
               debug (DB_LOG, "  NAK oldest or rseq", 0, rseq);
               nak_oldest_unacked (k, rseq);
#endif
            }
            else
            {
               debug (DB_MSG, "  resend earliest", 0, 0);
               resend (k, -1);
            }
            return (X_OK);
         }
         break;

      case 2:                  /* Type 2, 12-bit checksum */
         i = xunchar (*pbc) << 6 | xunchar (pbc[1]);
         ok = (i == chk2 (qdf, k));
#ifdef DEBUG
         if (ok && xerror ())
            ok = 0;
#endif /* DEBUG */
         if (!ok)
         {                      /* No match */
            debug (DB_CHR, "12-bit checksum ERROR rtyp", 0, rtyp);
            debug (DB_LOG, "  rseq", 0, rseq);
            debug (DB_PKT, "  k->ipktbuf", k->ipktbuf, 0);
            if (rtyp == 'E')
            {                   /* Allow E packets to have type 1 */
               int j;
               j = datalen;
               pdf[j++] = pbc[0];
               pdf[j] = '\0';
               if (xunchar (pbc[1]) == chk1 (qdf, k))
                  break;
               else
                  pdf[--j] = '\0';
            }
            if (k->what == W_RECV)
            {
#ifdef USE_NAK_OLDEST_UNACKED
               debug (DB_LOG, "  NAK oldest or rseq", 0, rseq);
               nak_oldest_unacked (k, rseq);
#endif
            }
            else
            {
               debug (DB_MSG, "  resend earliest", 0, 0);
               resend (k, -1);
            }
            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 (qdf, k));
#ifdef DEBUG
         if (ok && xerror ())
         {
            ok = 0;
            debug (DB_MSG, "INPUT CRC ERROR INJECTED", 0, 0);
         }
#endif /* DEBUG */
         if (!ok)
         {
            debug (DB_MSG, "Packet blk chk3 bad", 0, 0);
            debug (DB_LOG, "  rseq", 0, rseq);

            if (rtyp == 'E')
            {                   /* Allow E packets to have type 1 */
               int j;
               j = datalen;
               pdf[j++] = pbc[0];
               pdf[j++] = pbc[1];
               pdf[j] = '\0';
               if (xunchar (pbc[2]) == chk1 (qdf, k))
                  break;
               else
               {
                  j -= 2;
                  pdf[j] = '\0';
               }
            }
            if (k->what == W_RECV)
            {
#ifdef USE_NAK_OLDEST_UNACKED
               debug (DB_LOG, "  NAK oldest or rseq", 0, rseq);
               nak_oldest_unacked (k, rseq);
#endif
            }
            else
            {
               debug (DB_MSG, "  resend earliest", 0, 0);
               resend (k, -1);
            }
            return (X_OK);
         }
      }

      // now the packet has a good block check
      debug (DB_MSG, "Packet blk chk good", 0, 0);
      debug (DB_CHR, "  rtyp", 0, rtyp);
      debug (DB_LOG, "  rseq", 0, rseq);
      debug (DB_LOG, "  k->r_seq", 0, k->r_seq);
      debug (DB_LOG, "  k->state", 0, k->state);
      debug (DB_LOG, "  chklen", 0, chklen);
      debug (DB_LOG, "  datalen", 0, datalen);

      if (rtyp == 'E')          /* (AND CLOSE FILES?) */
      {
         debug (DB_MSG, "error msg received from other kermit", 0, 0);
         debug (DB_PKT, "EPKT", pdf, 0);
         return (X_ERROR);
      }

      if (k->what == W_SEND)    /* Sending, check for ACK */
      {
         if (rtyp != 'Y')
         {
            int ret;

            debug (DB_CHR, "rtyp not Y, rtyp", 0, rtyp);

            if (k->state == S_DATA &&
                rtyp == 'N' && rseq == ((k->r_seq + 1) & 63))
            {
               debug (DB_MSG,
                      "FYI got NAK for packet just after last one sent", 0,
                      0);
#ifdef DEBUG
               show_sslots (k);
#endif

# if 0
               debug (DB_MSG, "W_SEND Freeing all send slots", 0, 0);
               for (i = 0; i < k->wslots; i++)
                  free_sslot (k, i);
               k->sw_full = 0;
# endif
            }

            if (nused_sslots (k) == 0)
            {
               debug (DB_LOG, "sending requested rseq", 0, rseq);

               nxtpkt (k);
               if (k->s_seq != rseq)
               {
                  debug (DB_LOG, "error confused s_seq", 0, k->s_seq);
                  debug (DB_LOG, "  rseq", 0, rseq);
                  epkt ("EKSW W_SEND confused", k);
                  exit (1);
                  return (X_ERROR);
               }
               buf = get_sslot (k, &s_slot);    // get a new send slot
               k->s_pw[k->s_seq] = s_slot;

               rc = sdata (k, r);       /* Send next data packet */
               if (rc == X_ERROR)
                  return (rc);
               if (rc == 0)
               {                /* If there was no data to send */
                  k->closef (k, 0, 1);  /* Close input file */
                  k->state = S_EOF;     /* And wait for ACK */
                  r->rstatus = S_EOF;
                  k->s_seq--;
                  if (k->s_seq < 0)
                     k->s_seq += 64;
                  free_sslot (k, s_slot);
               }                /* Otherwise stay in data state */
               k->r_seq = k->s_seq;     /* Sequence number to wait for */
               return (X_OK);
            }
            else
            {
               debug (DB_LOG, "6: resending rseq", 0, rseq);
               // resend will send rseq if it's in the send table
               // otherwise it will resend earliest unacked packet
               ret = resend (k, rseq);
               debug (DB_LOG, "ret", 0, ret);
               return (ret);
            }
         }

         // we're sending, received an ACK and packet block check OK

         s_slot = k->s_pw[rseq];
         if (s_slot < 0 || s_slot >= k->wslots)
         {
            debug (DB_LOG, "ignoring ack -- not in table rseq", 0, rseq);
#ifdef DEBUG
            show_sslots (k);
#endif
            return (X_OK);
         }

         // set ACK'd flag for that send slot
         k->opktinfo[s_slot].flg = 1;

         // remove earliest and subsequent
         free_sslot_easca (k);

#ifdef DEBUG
         // check that send table sequence numbers are in order
         chk_sseq_nos (k);
#endif

         if (k->state == S_DATA)
         {                      /* ACK to Data packet? */
            if (k->cancel ||    /* Cancellation requested by caller? */
                *pdf == 'X' || *pdf == 'Z')
            {                   /* Or by receiver? */
               k->closef (k, 0, 1);     /* Close input file */
               nxtpkt (k);      /* Next packet sequence number */
               buf = get_sslot (k, &s_slot);    // get a new send slot
               k->s_pw[k->s_seq] = s_slot;

               debug (DB_LOG, "Cancellation: sending Z pkt s_seq", 0,
                      k->s_seq);
               debug (DB_LOG, "  s_slot", 0, s_slot);

               rc = spkt ('Z', k->s_seq, 0, (UCHAR *) 0, k);
               debug (DB_LOG, "  rc", 0, rc);
               if (rc != X_OK)
               {
                  return (rc);
               }
               if (*pdf == 'Z' || k->cancel == I_GROUP)
               {                /* Cancel Group? */
                  debug (DB_MSG, "Group Cancel (Send)", 0, 0);
                  while (*(k->filelist))
                  {             /* Go to end of file list */
                     debug (DB_LOG, "Skip", *(k->filelist), 0);
                     (k->filelist)++;
                  }
               }
               k->state = S_EOF;        /* Wait for ACK to EOF */
               r->rstatus = S_EOF;
               k->r_seq = k->s_seq;     /* Sequence number of packet we want */
               return (X_OK);
            }
         }
         r->sofar = r->sofar_rumor;

         debug (DB_LOG, "end of W_SEND rseq", 0, rseq);
         debug (DB_LOG, "  k->r_seq", 0, k->r_seq);
         debug (DB_LOG, "  k->state", 0, k->state);
         debug (DB_CHR, "  rtyp", 0, rtyp);
      }                         // if (k->what == W_SEND)
   }                            // if(k->do_rxd)

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

   case S_INIT:                /* Got other Kermit's parameters */
   case S_EOF:                 /* got ack to Z packet */
      if (k->state == S_INIT)
      {                         /* Got ACK to S packet? */
         debug (DB_MSG, "S_INIT", 0, 0);
         spar (k, pdf, datalen);        /* Set negotiated parameters */
         debug (DB_CHR, "Parity", 0, k->parity);
         debug (DB_LOG, "Ebqflg", 0, k->ebqflg);
         debug (DB_CHR, "Ebq", 0, k->ebq);
      }
      else
      {
         debug (DB_LOG, "S_EOF rseq", 0, rseq);

# if 0                          // don't use k->r_seq to switch states -- use nused_sslots()
         debug (DB_LOG, "S_EOF k->r_seq", 0, k->r_seq);
         if (rseq != k->r_seq)  // keep reading until last packet ACKed
            return (X_OK);
# endif

         if ((i = nused_sslots (k)) > 0)
         {
            debug (DB_LOG, "keep reading until all sslots free.  nused", 0,
                   i);
#ifdef DEBUG
            show_sslots (k);
#endif
            return (X_OK);
         }
      }

      nxtpkt (k);               /* Get next packet number etc */

      k->filename = *(k->filelist);     /* Get next filename */
      if (k->filename)
      {                         /* If there is one */
         int i;
         for (i = 0; i < FN_MAX; i++)
         {                      /* Copy name to result struct 
                                 */
            r->filename[i] = k->filename[i];
            if (!(r->filename[i]))
               break;
         }
         (k->filelist)++;
         debug (DB_LOG, "Filename", k->filename, 0);
         if ((rc = (k->openf) (k, k->filename, 1)) != X_OK)     /* Try to open */
         {
            debug (DB_LOG, "k->openf failed rc", 0, rc);
            return (rc);
         }

         encstr (k->filename, k, r);    /* Encode the name for transmission */

         buf = get_sslot (k, &s_slot);  // get a new send slot
         k->s_pw[k->s_seq] = s_slot;

         debug (DB_LOG, "sending F pkt k->s_seq", 0, k->s_seq);
         debug (DB_LOG, "sending F pkt s_slot", 0, s_slot);
         k->sw_full = 0;
         if ((rc = spkt ('F', k->s_seq, -1, k->xdata, k)) != X_OK)
         {
            return (rc);        /* Send F packet */
         }
         r->sofar = 0L;
         r->sofar_rumor = 0L;
         k->state = S_FILE;     /* Wait for ACK */
         r->rstatus = S_FILE;
      }
      else
      {                         /* No more files - we're done */

# if 0                          // this was a hack but should not be needed

         int nused = nused_sslots (k);
         debug (DB_LOG, "want to send B pkt nused", 0, nused);

         if (nused == k->wslots)
         {
            short rm_sseq;
            short rm_sslot;

            rm_sseq = earliest_sseq (k);
            if (rm_sseq < 0 || rm_sseq >= k->wslots)
            {
               debug (DB_LOG, "want to send B pkt error rm_sseq", 0, rm_sseq);
               return (X_ERROR);
            }

            rm_sslot = k->s_pw[rm_sseq];
            if (rm_sslot < 0 || rm_sslot >= k->wslots)
            {
               debug (DB_LOG, "want to send B pkt error rm_sslot", 0,
                      rm_sslot);
               debug (DB_LOG, "  rm_sseq", 0, rm_sseq);
               return (X_ERROR);
            }

            free_sslot (k, rm_sslot);
         }
# endif

         // all sslots are supposed to be free at this point

         buf = get_sslot (k, &s_slot);  // get a new send slot
         if (s_slot < 0 || s_slot >= k->wslots)
         {
            debug (DB_LOG, "want to send B pkt error s_slot", 0, s_slot);
            debug (DB_LOG, "  k->s_seq", 0, k->s_seq);
            return (X_ERROR);
         }
         k->s_pw[k->s_seq] = s_slot;

         debug (DB_LOG, "sending B pkt k->s_seq", 0, k->s_seq);
         debug (DB_LOG, "  s_slot", 0, s_slot);

         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 to B packet, end of transmission */
         r->rstatus = S_EOT;
      }
      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, r)) != X_OK)       /* Yes, send Attribute packet */
         {
            debug (DB_LOG, "SATTR failed rc", 0, rc);
            return (rc);
         }
         k->state = S_ATTR;     /* And wait for its ACK */
         r->rstatus = S_ATTR;
         did_a_pkt = 1;
      }
      else
      {
         did_a_pkt = 0;
      }

      if (did_a_pkt == 0)
      {
         /* No A packets - send first data */

         buf = get_sslot (k, &s_slot);  // get a new send slot
         k->s_pw[k->s_seq] = s_slot;

         debug (DB_LOG, "sending first D k->s_seq", 0, k->s_seq);
         debug (DB_LOG, "  s_slot", 0, s_slot);

         rc = sdata (k, r);     /* Send next data packet */
         if (rc == X_ERROR)
            return (rc);
         if (rc == 0)
         {
            /* File is empty so send EOF packet */
            buf = get_sslot (k, &s_slot);       // get a new send slot
            k->s_pw[k->s_seq] = s_slot;
            debug (DB_LOG, "empty file: sending Z pkt k->s_seq", 0, k->s_seq);
            debug (DB_LOG, "  s_slot", 0, s_slot);
            if ((rc = spkt ('Z', k->s_seq, 0, (UCHAR *) 0, k)) != X_OK)
            {
               return (rc);
            }
            k->closef (k, 0, 1);        /* Close input file */
            k->state = S_EOF;   /* Wait for ACK to EOF */
            r->rstatus = S_EOF;
         }
         else
         {                      /* Sent some data */
            k->state = S_DATA;  /* Wait for ACK to first data */
            r->rstatus = S_DATA;
         }
      }                         // if(did_a_pkt==0)

      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 */
      if (k->state == S_ATTR)
      {
         /*
          * CHECK ATTRIBUTE RESPONSE 
          */
         /*
          * IF REJECTED do the right thing...
          * Left as an exersise for the reader...
          */
         k->state = S_DATA;
         r->rstatus = S_DATA;
      }

      buf = get_sslot (k, &s_slot);     // get a new send slot
      if (s_slot < 0)
      {
         debug (DB_LOG, "window full k->state", 0, k->state);
         k->sw_full = 1;
         return (X_OK);
      }
      nxtpkt (k);               /* Get next packet number */
      k->s_pw[k->s_seq] = s_slot;

      k->sw_full = 0;

      debug (DB_LOG, "S_DATA sending k->s_seq", 0, k->s_seq);
      debug (DB_LOG, "  s_slot", 0, s_slot);

      rc = sdata (k, r);        /* Send first or next data packet */

      debug (DB_LOG, "S_DATA sdata rc", 0, rc);
      debug (DB_LOG, "  k->s_seq", 0, k->s_seq);

      if (rc == X_ERROR)
         return (rc);

      if (rc == 0)
      {                         /* If there was no data to send */
         k->closef (k, 0, 1);   /* Close input file */

         debug (DB_LOG, "NUSED_SSLOTS", 0, nused_sslots (k));

         k->state = S_EOF;      /* And wait for ACK to Z pkt */
         r->rstatus = S_EOF;

         // sdata() above did not send D pkt so send Z pkt in its place
         debug (DB_LOG, "EOF so sending Z pkt k->s_seq", 0, k->s_seq);
         debug (DB_LOG, "  s_slot", 0, s_slot);

         if ((rc = spkt ('Z', k->s_seq, 0, (UCHAR *) 0, k)) != X_OK)
            return (rc);

      }                         /* 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 */
      debug (DB_MSG, "S_EOT X_DONE", 0, 0);
      return (X_DONE);          /* (or X_ERROR) */

   case R_WAIT:                /* Waiting for the S packet */
      debug (DB_CHR, "R_WAIT rtyp", 0, rtyp);
      debug (DB_LOG, "  rseq", 0, rseq);
      debug (DB_LOG, "  what", 0, k->what);
      if (rtyp == 'S')
      {                         /* Got it */
         spar (k, pdf, datalen);        /* Set parameters from it */

         debug (DB_MSG, "R_WAIT rtyp==S after spar", 0, 0);
         debug (DB_CHR, "  Parity", 0, k->parity);
         debug (DB_LOG, "  Ebqflg", 0, k->ebqflg);
         debug (DB_CHR, "  Ebq", 0, k->ebq);

         rc = rpar (k, 'Y');    /* ACK with my parameters */

         debug (DB_LOG, "R_WAIT rtyp==S after rpar rc", 0, rc);
         debug (DB_LOG, "  k->capas & CAP_LP", 0, k->capas & CAP_LP);
         debug (DB_LOG, "  k->capas & CAP_SW", 0, k->capas & CAP_SW);
         debug (DB_LOG, "  k->capas & CAP_AT", 0, k->capas & CAP_AT);
         debug (DB_LOG, "  k->capas & CAP_RS", 0, k->capas & CAP_RS);
         debug (DB_LOG, "  k->capas & CAP_LS", 0, k->capas & CAP_LS);
         debug (DB_CHR, "  k->ebq           ", 0, k->ebq);
         debug (DB_LOG, "  k->ebqflg        ", 0, k->ebqflg);
         debug (DB_LOG, "  k->parity        ", 0, k->parity);
         debug (DB_LOG, "  k->s_eom         ", 0, k->s_eom);
         debug (DB_LOG, "  k->r_timo        ", 0, k->r_timo);
         debug (DB_LOG, "  k->s_timo        ", 0, k->s_timo);
         debug (DB_CHR, "  k->r_ctlq        ", 0, k->r_ctlq);
         debug (DB_CHR, "  k->s_ctlq        ", 0, k->s_ctlq);
         debug (DB_CHR, "  k->rptq          ", 0, k->rptq);
         debug (DB_LOG, "  k->rptflg        ", 0, k->rptflg);
         debug (DB_LOG, "  k->bct           ", 0, k->bct);
         debug (DB_LOG, "  k->bcta3           ", 0, k->bcta3);
         debug (DB_LOG, "  k->r_maxlen      ", 0, k->r_maxlen);
         debug (DB_LOG, "  k->s_maxlen      ", 0, k->s_maxlen);
         debug (DB_LOG, "  k->wslots        ", 0, k->wslots);
         debug (DB_LOG, "  k->binary        ", 0, k->binary);
         debug (DB_LOG, "  k->retry         ", 0, k->retry);

         if (rc != X_OK)
         {
            debug (DB_MSG, "R_WAIT error rpar(Y) failed", 0, 0);
            return (X_ERROR);   /* I/O error, quit. */
         }
         k->state = R_FILE;     /* All OK, switch states */
         r->rstatus = R_FILE;
         k->what = W_RECV;
      }
      else if (k->what == W_GET)
      {
         debug (DB_MSG, "  what==W_GET so resending R-packet", 0, 0);
         rc = resend (k, 0);
      }
      else
      {
         debug (DB_MSG, "  unexpected packet so send NAK for seq 0", 0, 0);
         rc = nak (k, 0, -1);
      }
      if (rc != X_OK)
         debug (DB_LOG, "R_WAIT rc not X_OK: rc", 0, rc);
      return (rc);

   case R_FILE:                /* Want an F or B packet, may get Z and S pkt */
      debug (DB_CHR, "R_FILE rtyp", 0, rtyp);
      if (rtyp == 'F')
      {                         /* File name */
         if ((rc = decode (k, r, 0, pdf, -1)) == X_OK)  /* Decode and save */
            k->state = R_ATTR;  /* Switch to next state */
         r->rstatus = k->state;
         debug (DB_LOG, "R_FILE decode rc", 0, rc);
         debug (DB_LOG, "R_FILE FILENAME", r->filename, 0);
         if (rc == X_OK)
         {                      /* All OK so far */
            r->filedate[0] = '\0';      /* No file date yet */
            r->filesize = 0L;   /* Or file size */
            r->sofar = 0L;      /* Or bytes transferred yet */
            r->sofar_rumor = 0L;        /* Or bytes transferred yet */
            rc = ack (k, rseq, r->filename);    /* so ACK the F packet */
         }
         else
         {
            epkt ("eksw Filename error", k);    /* Error decoding filename */
            return (rc);
         }
      }
      else if (rtyp == 'B')
      {                         /* Break, end of transaction */
         ack (k, rseq, (UCHAR *) 0);
         ack (k, rseq, (UCHAR *) 0);
         rc = (ack (k, rseq, (UCHAR *) 0) == X_OK) ? X_DONE : X_ERROR;
      }
      else if (rtyp == 'Z')
      {                         /* End of file again */
         rc = ack (k, rseq, (UCHAR *) 0);
      }
      else if (rtyp == 'S')
      {                         /* got S pkt again */
         spar (k, pdf, datalen);        /* Set parameters from it */

         debug (DB_MSG, "R_FILE rtyp==S again", 0, 0);
         debug (DB_CHR, "  Parity", 0, k->parity);
         debug (DB_LOG, "  Ebqflg", 0, k->ebqflg);
         debug (DB_CHR, "  Ebq", 0, k->ebq);

         rc = rpar (k, 'Y');    /* ACK with my parameters */

         debug (DB_LOG, "R_FILE after rpar rc", 0, rc);
         if (rc != X_OK)
         {
            debug (DB_MSG, "R_FILE error rpar(Y) failed", 0, 0);
            return (X_ERROR);   /* I/O error, quit. */
         }
         k->state = R_FILE;     /* All OK, switch states */
         r->rstatus = R_FILE;
         k->what = W_RECV;
      }
      else
      {
         rc = X_ERROR;
      }
      debug (DB_LOG, "rc", 0, rc);
      return (rc);

   case R_ATTR:                /* Want A, D, or Z packet */
      debug (DB_CHR, "R_ATTR rtyp", 0, rtyp);
      if (rtyp == 'A')
      {                         /* Attribute packet */
         int x;
         x = gattr (k, pdf, r); /* Read the attributes */
         if (x > -1)
            k->binary = x;
         ack (k, rseq, (UCHAR *) "Y");  /* Always accept the file */
         return (X_OK);
      }
      else if (rtyp == 'D')
      {                         /* First data packet */
         k->obufpos = 0;        /* Initialize output buffer */
         k->filename = r->filename;
         r->sofar = 0L;
         r->sofar_rumor = 0L;
         if ((rc = (*(k->openf)) (k, r->filename, 2)) == X_OK)
         {
            k->state = R_DATA;  /* Switch to Data state */
            r->rstatus = k->state;
            rc = handle_good_rpkt (k, r, rseq, pdf);
         }
         else
         {
            debug (DB_MSG, "eksw cannot open output file", 0, 0);
            epkt ("eksw cannot open output file", k);
            return (rc);
         }

         if (rc == X_OK)
         {
         }
         else
         {
            debug (DB_MSG, "eksw Error writing data to file", 0, 0);
            epkt ("eksw Error writing data to file", k);
         }
         return (rc);
      }
      else if (rtyp == 'Z')
      {                         /* Empty file */
         debug (DB_LOG, "R_ATTR empty file", r->filename, 0);
         k->obufpos = 0;        /* Initialize output buffer */
         k->filename = r->filename;
         r->sofar = 0L;         /* Open and close the file */
         r->sofar_rumor = 0L;   /* Open and close the file */
         if ((rc = (*(k->openf)) (k, r->filename, 2)) == X_OK)
         {
            if (((rc = (*(k->closef)) (k, *pdf, 2)) == X_OK))
            {
               k->state = R_FILE;
               rc = ack (k, rseq, s);
            }
            else
            {
               debug (DB_MSG, "eksw Error closing empty file", 0, 0);
               epkt ("eksw Error closing empty file", k);
               return (rc);
            }
         }
         else
         {
            debug (DB_LOG, "eksw File refused or cannot be opened rc", 0, rc);
            epkt ("eksw File refused or cannot be opened", k);
            return (rc);
         }
         r->rstatus = k->state;
         return (X_OK);
      }
      else if (rtyp == 'F')     // received F pkt again
      {
         ack (k, rseq, (UCHAR *) 0);
         return (X_OK);
      }
      else
      {
         debug (DB_CHR, "R_ATTR error 3 unexpected packet rtyp", 0, rtyp);
         epkt ("eksw R_ATTR error 3 unexpected packet type", k);
         exit (1);
         return (X_ERROR);
      }
      break;

   case R_DATA:                /* Want a D or Z packet */
      debug (DB_CHR, "R_DATA rtyp", 0, rtyp);
      debug (DB_LOG, "R_DATA rseq", 0, rseq);
      if (rtyp == 'D')
      {                         /* Data */
         rc = handle_good_rpkt (k, r, rseq, pdf);
         debug (DB_LOG, "R_DATA hgr rc", 0, rc);
      }
      else if (rtyp == 'Z')
      {                         /* End of file */
         debug (DB_LOG, "R_DATA Z pkt obufpos", 0, k->obufpos);
         flush_to_file (k, r);
         if (((rc = (*(k->closef)) (k, *pdf, 2)) == X_OK) && (rc == X_OK))
            k->state = R_FILE;
         debug (DB_LOG, "R_DATA closef rc", 0, rc);
         r->rstatus = k->state;
      }
      else
      {
         debug (DB_CHR, "R_DATA error 4 unexpected packet rtyp", 0, rtyp);
         epkt ("eksw error 4 unexpected packet type", k);
         exit (1);
         return (X_ERROR);
      }

      if (rc == X_OK)
      {
         if (rtyp == 'Z')
         {
            rc = ack (k, rseq, s);      // usual ACK to D or Z packet
         }
      }
      else
      {
         debug (DB_CHR, "Error 12: rc != X_OK rtyp", 0, rtyp);
         epkt (rtyp == 'Z' ? "eksw Can't close file" : "eksw hgr failed", k);
      }

      return (rc);

   case R_ERROR:               /* Canceled from above */
      debug (DB_LOG, "R_ERROR error so sending E pkt k->state", 0, k->state);
   default:
      debug (DB_LOG, "  default error so sending E pkt k->state", 0,
             k->state);
      epkt (msg, k);
      return (X_ERROR);
   }

   // not supposed to get here
   debug (DB_LOG, "Kermit logic error k->state", 0, k->state);
   exit (1);
   return (X_ERROR);            /* make compiler happy */
}
