[Cryptech-Commits] [sw/thirdparty/ekermit] 01/03: Initial commit of unmodified E-Kermit 1.7

git at cryptech.is git at cryptech.is
Tue Jun 21 02:53:23 UTC 2016


This is an automated email from the git hooks/post-receive script.

paul at psgd.org pushed a commit to branch master
in repository sw/thirdparty/ekermit.

commit 04d48e513f2094345eb697c0a34345daff63a87b
Author: Paul Selkirk <paul at psgd.org>
AuthorDate: Thu Jun 16 15:42:58 2016 -0400

    Initial commit of unmodified E-Kermit 1.7
---
 COPYING       |   35 ++
 cdefs.h       |   33 ++
 debug.h       |   40 ++
 ek17.diff     |  232 +++++++++
 kermit.c      | 1619 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 kermit.c.diff |  139 +++++
 kermit.h      |  423 +++++++++++++++
 kermit.h.diff |   29 ++
 main.c        |  447 ++++++++++++++++
 main.c.diff   |   64 +++
 makefile      |   69 +++
 platform.h    |   12 +
 unix.h        |   12 +
 unixio.c      |  627 ++++++++++++++++++++++
 14 files changed, 3781 insertions(+)

diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..18edfbc
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,35 @@
+
+  E-Kermit 1.7 -- Embedded Kermit
+
+  Author:  Frank da Cruz
+  License: Revised 3-Clause BSD License
+
+  Copyright (C) 1995, 2011, 
+  Trustees of Columbia University in the City of New York.
+  All rights reserved.
+
+  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.
diff --git a/cdefs.h b/cdefs.h
new file mode 100644
index 0000000..e118a14
--- /dev/null
+++ b/cdefs.h
@@ -0,0 +1,33 @@
+#ifndef __CDEFS_H__
+#define __CDEFS_H__
+
+/*
+  By default, the internal routines of kermit.c are not static,
+  because this is not allowed in some embedded environments.
+  To have them declared static, define STATIC=static on the cc
+  command line.
+*/
+#ifdef XAC  /* HiTech's XAC cmd line is small */
+#define STATIC static
+#else /* XAC */
+#ifndef STATIC
+#define STATIC
+#endif /* STATIC */
+#endif	/* XAC */
+
+/*
+  By default we assume the compiler supports unsigned char and
+  unsigned long.  If not you can override these definitions on
+  the cc command line.
+*/
+#ifndef HAVE_UCHAR
+typedef unsigned char UCHAR;
+#endif /* HAVE_UCHARE */
+#ifndef HAVE_ULONG
+typedef unsigned long ULONG;
+#endif /* HAVE_ULONG */
+#ifndef HAVE_USHORT
+typedef unsigned short USHORT;
+#endif /* HAVE_USHORT */
+
+#endif /* __CDEFS_H__ */
diff --git a/debug.h b/debug.h
new file mode 100644
index 0000000..e1af40e
--- /dev/null
+++ b/debug.h
@@ -0,0 +1,40 @@
+#ifndef NODEBUG				/* NODEBUG inhibits debugging */
+#ifndef DEBUG				/* and if DEBUG not already defined */
+#ifndef MINSIZE				/* MINSIZE inhibits debugging */
+#ifndef DEBUG
+#define DEBUG
+#endif /* DEBUG */
+#endif /* MINSIZE */
+#endif /* DEBUG */
+#endif /* NODEBUG */
+
+#ifdef DEBUG				/* Debugging included... */
+/* dodebug() function codes... */
+#define DB_OPN 1			/* Open log */
+#define DB_LOG 2			/* Write label+string or int to log */
+#define DB_MSG 3			/* Write message to log */
+#define DB_CHR 4			/* Write label + char to log */
+#define DB_PKT 5			/* Record a Kermit packet in log */
+#define DB_CLS 6			/* Close log */
+
+void dodebug(int, UCHAR *, UCHAR *, long); /* Prototype */
+/*
+  dodebug() is accessed throug a macro that:
+   . Coerces its args to the required types.
+   . Accesses dodebug() directly or thru a pointer according to context.
+   . Makes it disappear entirely if DEBUG not defined.
+*/
+#ifdef KERMIT_C
+/* In kermit.c we debug only through a function pointer */
+#define debug(a,b,c,d) \
+if(*(k->dbf))(*(k->dbf))(a,(UCHAR *)b,(UCHAR *)c,(long)(d))
+
+#else  /* KERMIT_C */
+/* Elsewhere we can call the debug function directly */
+#define debug(a,b,c,d) dodebug(a,(UCHAR *)b,(UCHAR *)c,(long)(d))
+#endif /* KERMIT_C */
+
+#else  /* Debugging not included... */
+
+#define debug(a,b,c,d)
+#endif /* DEBUG */
diff --git a/ek17.diff b/ek17.diff
new file mode 100644
index 0000000..6853edf
--- /dev/null
+++ b/ek17.diff
@@ -0,0 +1,232 @@
+*** ../../ek16/kermit.c	2011-03-30 12:40:09.705176000 -0400
+--- kermit.c	2011-06-06 16:24:13.034202000 -0400
+***************
+*** 1,8 ****
+  #define KERMIT_C
+  /*
+    Embedded Kermit protocol module
+!   Version: 1.6
+!   Most Recent Update: Wed Mar 30 12:39:11 2011
+  
+    No stdio or other runtime library calls, no system calls, no system 
+    includes, no static data, and no global variables in this module.
+--- 1,8 ----
+  #define KERMIT_C
+  /*
+    Embedded Kermit protocol module
+!   Version: 1.7
+!   Most Recent Update: Mon Jun  6 15:36:26 2011
+  
+    No stdio or other runtime library calls, no system calls, no system 
+    includes, no static data, and no global variables in this module.
+***************
+*** 98,104 ****
+  
+      int i, j, rc;			/* Workers */
+      int datalen;                        /* Length of packet data field */
+-     int bctu;				/* Block check type for this packet */
+      UCHAR *p;                           /* Pointer to packet data field */
+      UCHAR *q;                           /* Pointer to data to be checked */
+      UCHAR *s;				/* Worker string pointer */
+--- 98,103 ----
+***************
+*** 313,319 ****
+          }
+  	debug(DB_MSG,"HDR CHKSUM OK",0,0);
+          p[2] = c;                       /* Put checksum back */
+!         datalen = xunchar(p[0])*95 + xunchar(p[1]) - k->bct; /* Data length */
+          p += 3;                         /* Fix data pointer */
+          k->ipktinfo[r_slot].dat = p;	/* Permanent record of data pointer */
+      } else {                            /* Regular packet */
+--- 312,319 ----
+          }
+  	debug(DB_MSG,"HDR CHKSUM OK",0,0);
+          p[2] = c;                       /* Put checksum back */
+! 	/* Data length */
+!         datalen = xunchar(p[0])*95 + xunchar(p[1]) - ((k->bctf) ? 3 : k->bct);
+          p += 3;                         /* Fix data pointer */
+          k->ipktinfo[r_slot].dat = p;	/* Permanent record of data pointer */
+      } else {                            /* Regular packet */
+***************
+*** 323,334 ****
+--- 323,342 ----
+      }
+  #endif /* F_LP */
+  #ifdef F_CRC
++     if (k->bctf) {			/* FORCE 3 */
++ 	chklen = 3;
++     } else {
+  	if (t == 'S' || k->state == S_INIT) { /* S-packet was retransmitted? */
++ 	    if (q[10] == '5') {		/* Block check type requested is 5 */
++ 		k->bctf = 1;		/* FORCE 3 */
++ 		chklen = 3;
++ 	    }
+  	    chklen = 1;			/* Block check is always type 1 */
+  	    datalen = k->ipktinfo[r_slot].len - 3; /* Data length */
+  	} else {
+  	    chklen = k->bct;
+  	}
++     }
+  #else
+      chklen = 1;				/* Block check is always type 1 */
+      datalen = k->ipktinfo[r_slot].len - 3; /* Data length */
+***************
+*** 1031,1036 ****
+--- 1039,1045 ----
+          if ((k->bct < 1) || (k->bct > 3))
+  #endif /* F_CRC */
+  	  k->bct = 1;
++ 	if (k->bctf) k->bct = 3;
+      }
+      if (datalen >= 9) {                 /* Repeat counts */
+          if ((s[9] > 32 && s[9] < 63) || (s[9] > 95 && s[9] < 127)) {
+***************
+*** 1120,1126 ****
+        d[ 6] = k->ebq = '&';           /* I need to request it */
+      else                                /* else just agree with other Kermit */
+        d[ 6] = k->ebq;
+!     d[ 7] = k->bct + '0';               /* Block check type */
+      d[ 8] = k->rptq;			/* Repeat prefix */
+      d[ 9] = tochar(k->capas);           /* Capability bits */
+      d[10] = tochar(k->window);          /* Window size */
+--- 1129,1138 ----
+        d[ 6] = k->ebq = '&';           /* I need to request it */
+      else                                /* else just agree with other Kermit */
+        d[ 6] = k->ebq;
+!     if (k->bctf)			/* Block check type */
+!       d[7] = '5';			/* FORCE 3 */
+!     else
+!       d[7] = k->bct + '0';		/* Normal */
+      d[ 8] = k->rptq;			/* Repeat prefix */
+      d[ 9] = tochar(k->capas);           /* Capability bits */
+      d[10] = tochar(k->window);          /* Window size */
+***************
+*** 1136,1143 ****
+--- 1148,1157 ----
+  #endif /* F_LP */
+  
+  #ifdef F_CRC
++     if (!(k->bctf)) {			/* Unless FORCE 3 */
+  	b = k->bct;
+  	k->bct = 1;			/* Always use block check type 1 */
++     }
+  #endif /* F_CRC */
+      switch (type) {
+        case 'Y':				/* This is an ACK for packet 0 */
+***************
+*** 1150,1156 ****
+--- 1164,1172 ----
+  	rc = -1;
+      }
+  #ifdef F_CRC
++     if (!(k->bctf)) {			/* Unless FORCE 3 */
+  	k->bct = b;
++     }
+  #endif /* F_CRC */
+      return(rc);                         /* Pass along return code. */
+  }
+***************
+*** 1510,1516 ****
+--- 1526,1534 ----
+  
+  STATIC void
+  epkt(char * msg, struct k_data * k) {
++     if (!(k->bctf)) {			/* Unless FORCE 3 */
+  	k->bct = 1;
++     }
+      (void) spkt('E', 0, -1, (UCHAR *) msg, k);
+  }
+  
+*** ../../ek16/kermit.h	2011-03-30 13:13:04.814335000 -0400
+--- kermit.h	2011-06-06 15:36:54.700435000 -0400
+***************
+*** 1,7 ****
+  #ifndef __KERMIT_H__
+  #define __KERMIT_H__
+  
+! #define VERSION "1.6"			/* Kermit module version number */
+  
+  /*
+    kermit.h -- Symbol and struct definitions for embedded Kermit.
+--- 1,7 ----
+  #ifndef __KERMIT_H__
+  #define __KERMIT_H__
+  
+! #define VERSION "1.7"			/* Kermit module version number */
+  
+  /*
+    kermit.h -- Symbol and struct definitions for embedded Kermit.
+***************
+*** 388,393 ****
+--- 388,394 ----
+      int zincnt;				/* Input buffer position */
+      int zinlen;				/* Length of input file buffer */
+      UCHAR * zinptr;			/* Pointer to input file buffer */
++     int bctf;				/* Flag to force type 3 block check */
+      int dummy;
+  };
+  
+*** ../../ek16/main.c	2011-03-30 12:40:53.830806000 -0400
+--- main.c	2011-06-06 15:33:45.997789000 -0400
+***************
+*** 124,130 ****
+  #endif /* RECVONLY */
+      fprintf(stderr," -p [neoms]   Parity: none, even, odd, mark, space\n");
+  #ifdef F_CRC
+!     fprintf(stderr," -b [123]     Block check type: 1, 2, or 3\n");
+  #endif /* F_CRC */
+      fprintf(stderr," -k           Keep incompletely received files\n");
+      fprintf(stderr," -B           Force binary mode\n");
+--- 124,130 ----
+  #endif /* RECVONLY */
+      fprintf(stderr," -p [neoms]   Parity: none, even, odd, mark, space\n");
+  #ifdef F_CRC
+!     fprintf(stderr," -b [1235]    Block check type: 1, 2, 3, or 5\n");
+  #endif /* F_CRC */
+      fprintf(stderr," -k           Keep incompletely received files\n");
+      fprintf(stderr," -B           Force binary mode\n");
+***************
+*** 219,225 ****
+  	    }
+  	    if (c == 'b') {
+  		check = atoi(*xargv);
+! 		if (check < 1 || check > 3)
+  		  fatal("Invalid block check",(char *)0,(char *)0);
+  #ifdef DEBUG
+  	    } else if (c == 'E') {
+--- 219,225 ----
+  	    }
+  	    if (c == 'b') {
+  		check = atoi(*xargv);
+! 		if (check < 1 || check > 5 || check == 4)
+  		  fatal("Invalid block check",(char *)0,(char *)0);
+  #ifdef DEBUG
+  	    } else if (c == 'E') {
+***************
+*** 338,344 ****
+      k.remote = remote;			/* Remote vs local */
+      k.binary = ftype;			/* 0 = text, 1 = binary */
+      k.parity = parity;                  /* Communications parity */
+!     k.bct = check;			/* Block check type */
+      k.ikeep = keep;			/* Keep incompletely received files */
+      k.filelist = cmlist;		/* List of files to send (if any) */
+      k.cancel = 0;			/* Not canceled yet */
+--- 338,344 ----
+      k.remote = remote;			/* Remote vs local */
+      k.binary = ftype;			/* 0 = text, 1 = binary */
+      k.parity = parity;                  /* Communications parity */
+!     k.bct = (check == 5) ? 3 : check;	/* Block check type */
+      k.ikeep = keep;			/* Keep incompletely received files */
+      k.filelist = cmlist;		/* List of files to send (if any) */
+      k.cancel = 0;			/* Not canceled yet */
+***************
+*** 367,372 ****
+--- 367,374 ----
+  #else
+      k.dbf    = 0;
+  #endif /* DEBUG */
++     /* Force Type 3 Block Check (16-bit CRC) on all packets, or not */
++     k.bctf   = (check == 5) ? 1 : 0;
+  
+  /* Initialize Kermit protocol */
+  
diff --git a/kermit.c b/kermit.c
new file mode 100644
index 0000000..e041c71
--- /dev/null
+++ b/kermit.c
@@ -0,0 +1,1619 @@
+#define KERMIT_C
+/*
+  Embedded Kermit protocol module
+  Version: 1.7
+  Most Recent Update: Mon Jun  6 15:36:26 2011
+
+  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 can't 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.
+
+  Author: Frank da Cruz.
+  As of version 1.6 of 30 March 2011, E-Kermit is Open Source software under
+  the Revised 3-Clause BSD license which follows.  E-Kermit 1.6 is identical
+  to version 1.51 except for the new license.
+
+  Author: Frank da Cruz.
+
+  Copyright (C) 1995, 2011, 
+  Trustees of Columbia University in the City of New York.
+  All rights reserved.
+
+  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.
+*/
+#include "cdefs.h"			/* C language defs for all modules */
+#include "debug.h"			/* Debugging */
+#include "kermit.h"			/* Kermit protocol definitions */
+
+#define zgetc() \
+((--(k->zincnt))>=0)?((int)(*(k->zinptr)++)&0xff):(*(k->readf))(k)
+
+/* 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 *);
+int STATIC spkt(char, short, int, UCHAR *, struct k_data *);
+int STATIC ack(struct k_data *, short, UCHAR * text);
+int STATIC nak(struct k_data *, short, short);
+int STATIC chk1(UCHAR *, struct k_data *);
+STATIC USHORT chk2(UCHAR *, struct k_data *);
+#ifdef F_CRC
+STATIC USHORT chk3(UCHAR *, struct k_data *);
+#endif /* F_CRC */
+void STATIC spar(struct k_data *, UCHAR *, int);
+int STATIC rpar(struct k_data *, char);
+int STATIC decode(struct k_data *, struct k_response *, short, UCHAR *);
+#ifdef F_AT
+int STATIC gattr(struct k_data *, UCHAR *, struct k_response *);
+int STATIC sattr(struct k_data *, struct k_response *);
+#endif /* F_AT */
+#ifndef RECVONLY
+int STATIC sdata(struct k_data *, struct k_response *);
+#endif /* RECVONLY */
+void STATIC epkt(char *, struct k_data *);
+int STATIC getpkt(struct k_data *, struct k_response *);
+int STATIC encstr(UCHAR *, struct k_data *, struct k_response *);
+void STATIC decstr(UCHAR *, struct k_data *, struct k_response *);
+void STATIC encode(int, int, struct k_data *);
+int STATIC nxtpkt(struct k_data *);
+int STATIC resend(struct k_data *);
+#ifdef DEBUG
+int xerror(void);
+#endif /* DEBUG */
+
+int					/* The kermit() function */
+kermit(short f,				/* Function code */
+       struct k_data *k,		/* The control struct */
+       short r_slot,			/* Received packet slot number */
+       int len,				/* Length of packet in slot */
+       char *msg,			/* Message for error packet */
+       struct k_response *r) {		/* Response struct */
+
+    int i, j, rc;			/* Workers */
+    int datalen;                        /* Length of packet data field */
+    UCHAR *p;                           /* Pointer to packet data field */
+    UCHAR *q;                           /* Pointer to data to be checked */
+    UCHAR *s;				/* Worker string pointer */
+    UCHAR c, t;                         /* Worker chars */
+    UCHAR pbc[4];                       /* Copy of packet block check */
+    short seq, prev;			/* Copies of sequence numbers */
+    short chklen;                       /* Length of packet block check */
+#ifdef F_CRC
+    unsigned int crc;                   /* 16-bit CRC */
+#endif /* F_CRC */
+    int ok;
+
+    debug(DB_MSG,"----------",0,0);	/* Marks each entry */
+    debug(DB_LOG,"f",0,f);
+    debug(DB_LOG,"state",0,k->state);
+    debug(DB_LOG,"zincnt",0,(k->zincnt));
+
+    if (f == 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 */
+
+        for (i = 0; i < P_WSLOTS; i++) { /* Packet info for each window slot */
+	    freerslot(k,i);
+	    freesslot(k,i);
+	}
+#ifdef F_TSW
+        for (i = 0; i < 64; i++) {	/* Packet finder array */
+	    k->r_pw[i] = -1;		/* initialized to "no packets yet" */
+	    k->s_pw[i] = -1;		/* initialized to "no packets yet" */
+	}
+#endif /* F_TSW */
+
+/* Initialize the k_data structure */    
+
+	for (i = 0; i < 6; i++)
+	  k->s_remain[i] = '\0';
+
+        k->state    = R_WAIT;		/* Beginning protocol state */
+	r->status   = 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_type   = k->r_type = 0;	/* Packet type */
+        k->r_timo   = P_R_TIMO;		/* Timeout interval for me to use */
+        k->s_timo   = P_S_TIMO;		/* Timeout for other Kermit to use */
+        k->r_maxlen = P_PKTLEN;         /* Maximum packet length */
+        k->s_maxlen = P_PKTLEN;         /* Maximum packet length */
+        k->window   = P_WSLOTS;		/* Maximum window slots */
+        k->wslots   = 1;		/* Current window slots */
+	k->zincnt   = 0;
+	k->dummy    = 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 */
+#ifdef F_LP
+          | CAP_LP                      /* Long packets */
+#endif /* F_LP */
+#ifdef F_SW
+            | CAP_SW                    /* Sliding windows */
+#endif /* F_SW */
+#ifdef F_AT
+              | CAP_AT                  /* Attribute packets */
+#endif /* F_AT */
+                ;
+
+	k->opktbuf[0] = '\0';		/* No packets sent yet. */
+	k->opktlen = 0;
+
+#ifdef F_CRC
+/* 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;
+#endif /* F_CRC */
+
+	return(X_OK);
+
+#ifndef RECVONLY
+    } else if (f == K_SEND) {
+	if (rpar(k,'S') != X_OK)	/* Send S packet with my parameters */
+	  return(X_ERROR);		/* I/O error, quit. */
+	k->state = S_INIT;		/* All OK, switch states */
+	r->status = S_INIT;
+	k->what = W_SEND;		/* Act like a sender */
+        return(X_OK);
+#endif /* RECVONLY */
+
+    } else if (f == K_STATUS) {         /* Status report requested. */
+        return(X_STATUS);               /* File name, date, size, if any. */
+
+    } else if (f == K_QUIT) {           /* You told me to quit */
+        return(X_DONE);                 /* so I quit. */
+
+    } else if (f == K_ERROR) {          /* Send an error packet... */
+        epkt(msg,k);
+	k->closef(k,0,(k->state == S_DATA) ? 1 : 2); /* Close file */
+        return(X_DONE);                 /* and quit. */
+
+    } else if (f != K_RUN) {            /* Anything else is an error. */
+        return(X_ERROR);
+    }
+    if (k->state == R_NONE)             /* (probably unnecessary) */
+      return(X_OK);
+
+/* If we're in the protocol, check to make sure we got a new packet */
+
+    debug(DB_LOG,"r_slot",0,r_slot);
+    debug(DB_LOG,"len",0,len);
+
+    if (r_slot < 0)			/* We should have a slot here */
+      return(K_ERROR);
+    else
+      k->ipktinfo[r_slot].len = len;	/* Copy packet length to ipktinfo. */
+    
+    if (len < 4) {			/* Packet obviously no good? */
+#ifdef RECVONLY
+	return(nak(k,k->r_seq,r_slot)); /* Send NAK for the packet we want */
+#else
+	if (k->what == W_RECV)		/* If receiving */
+	  return(nak(k,k->r_seq,r_slot)); /* Send NAK for the packet we want */
+	else				/* If sending */
+	  return(resend(k));		/* retransmit last packet. */
+#endif /* RECVONLY */
+    }
+
+/* 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;
+	}
+    }
+    p = &(k->ipktbuf[0][r_slot]);	/* Point to it */
+
+    q = p;                              /* Pointer to data to be checked */
+    k->ipktinfo[r_slot].len = xunchar(*p++); /* Length field */
+    seq = k->ipktinfo[r_slot].seq = xunchar(*p++); /* Sequence number */
+    t = k->ipktinfo[r_slot].typ = *p++;	/* Type */
+
+    if (
+#ifndef RECVONLY
+	(k->what == W_RECV) &&		/* Echo (it happens), ignore */
+#endif /* RECVONLY */
+	(t == 'N' || t  == 'Y')) {
+        freerslot(k,r_slot);
+        return(X_OK);
+    }
+    k->ipktinfo[r_slot].dat = p;	/* Data field, maybe */
+#ifdef F_LP
+    if (k->ipktinfo[r_slot].len == 0) {	/* Length 0 means long packet */
+        c = p[2];                       /* Get header checksum */
+        p[2] = '\0';
+        if (xunchar(c) != chk1(p-3,k)) {  /* Check it */
+            freerslot(k,r_slot);	/* Bad */
+	    debug(DB_MSG,"HDR CHKSUM BAD",0,0);
+#ifdef RECVONLY
+	    return(nak(k,k->r_seq,r_slot)); /* Send NAK */
+#else
+	    if (k->what == W_RECV)
+	      return(nak(k,k->r_seq,r_slot)); /* Send NAK */
+	    else
+	      return(resend(k));
+#endif /* RECVONLY */
+        }
+	debug(DB_MSG,"HDR CHKSUM OK",0,0);
+        p[2] = c;                       /* Put checksum back */
+	/* Data length */
+        datalen = xunchar(p[0])*95 + xunchar(p[1]) - ((k->bctf) ? 3 : k->bct);
+        p += 3;                         /* Fix data pointer */
+        k->ipktinfo[r_slot].dat = p;	/* Permanent record of data pointer */
+    } else {                            /* Regular packet */
+#endif /* F_LP */
+        datalen = k->ipktinfo[r_slot].len - k->bct - 2; /* Data length */
+#ifdef F_LP
+    }
+#endif /* F_LP */
+#ifdef F_CRC
+    if (k->bctf) {			/* FORCE 3 */
+	chklen = 3;
+    } else {
+	if (t == 'S' || k->state == S_INIT) { /* S-packet was retransmitted? */
+	    if (q[10] == '5') {		/* Block check type requested is 5 */
+		k->bctf = 1;		/* FORCE 3 */
+		chklen = 3;
+	    }
+	    chklen = 1;			/* Block check is always type 1 */
+	    datalen = k->ipktinfo[r_slot].len - 3; /* Data length */
+	} else {
+	    chklen = k->bct;
+	}
+    }
+#else
+    chklen = 1;				/* Block check is always type 1 */
+    datalen = k->ipktinfo[r_slot].len - 3; /* Data length */
+#endif /* F_CRC */
+    debug(DB_LOG,"bct",0,(k->bct));
+    debug(DB_LOG,"datalen",0,datalen);
+    debug(DB_LOG,"chkalen",0,chklen);
+
+#ifdef F_CRC
+    for (i = 0; i < chklen; i++)        /* Copy the block check */
+      pbc[i] = p[datalen+i];
+    pbc[i] = '\0';			/* Null-terminate block check string */
+#else
+    pbc[0] = p[datalen];
+    pbc[1] = '\0';
+#endif /* F_CRC */
+    p[datalen] = '\0';			/* and the packet DATA field. */
+#ifdef F_CRC
+    switch (chklen) {                   /* Check the block check  */
+      case 1:				/* Type 1, 6-bit checksum */
+#endif /* F_CRC */
+	ok = (xunchar(*pbc) == chk1(q,k));
+#ifdef DEBUG
+	if (ok && xerror()) ok = 0;
+#endif /* DEBUG */
+	if (!ok) {
+	    freerslot(k,r_slot);
+#ifdef RECVONLY
+	    nak(k,k->r_seq,r_slot);
+#else
+	    if (k->what == W_RECV)
+	      nak(k,k->r_seq,r_slot);
+	    else
+	      resend(k);
+#endif /* RECVONLY */
+	    return(X_OK);
+	}
+#ifdef F_CRC
+	break;
+
+      case 2:                         /* Type 2, 12-bit checksum */
+	i = xunchar(*pbc) << 6 | xunchar(pbc[1]);
+	ok = (i == chk2(q,k));
+#ifdef DEBUG
+	if (ok && xerror()) ok = 0;
+#endif /* DEBUG */
+	if (!ok) {			/* No match */
+	    if (t == 'E') {		/* Allow E packets to have type 1 */
+		int j;
+		j = datalen;
+		p[j++] = pbc[0];    
+		p[j] = '\0';
+		if (xunchar(pbc[1]) == chk1(q,k))
+		  break;
+		else
+		  p[--j] = '\0';
+	    }
+	    freerslot(k,r_slot);
+#ifdef RECVONLY
+	    nak(k,k->r_seq,r_slot);
+#else
+	    if (k->what == W_RECV)
+	      nak(k,k->r_seq,r_slot);
+	    else
+	      resend(k);
+#endif /* RECVONLY */
+	    return(X_OK);
+	}
+	break;
+
+      case 3:				/* Type 3, 16-bit CRC */
+	crc = (xunchar(pbc[0]) << 12)
+	  | (xunchar(pbc[1]) << 6)
+	    | (xunchar(pbc[2]));
+	ok = (crc == chk3(q,k));
+#ifdef DEBUG
+	if (ok && xerror()) {
+	    ok = 0;
+	    debug(DB_MSG,"CRC ERROR INJECTED",0,0);
+	} 
+#endif /* DEBUG */
+	if (!ok) {
+	    debug(DB_LOG,"CRC ERROR t",0,t);
+	    if (t == 'E') {		/* Allow E packets to have type 1 */
+		int j;
+		j = datalen;
+		p[j++] = pbc[0];    
+		p[j++] = pbc[1];
+		p[j] = '\0';
+		if (xunchar(pbc[2]) == chk1(q,k))
+		  break;
+		else { j -=2; p[j] = '\0'; }
+	    }
+	    freerslot(k,r_slot);
+#ifdef RECVONLY
+	    nak(k,k->r_seq,r_slot);
+#else
+	    if (k->what == W_RECV)
+	      nak(k,k->r_seq,r_slot);
+	    else
+	      resend(k);
+#endif /* RECVONLY */
+	    return(X_OK);
+	}
+    }
+#endif /* F_CRC */
+    if (t == 'E')			/* (AND CLOSE FILES?) */
+      return(X_ERROR);
+
+    prev = k->r_seq - 1;		/* Get sequence of previous packet */
+    if (prev < 0)
+      prev = 63;
+
+    debug(DB_LOG,"Seq",0,seq);
+    debug(DB_LOG,"Prev",0,prev);
+
+    if (seq == k->r_seq) {		/* Is this the packet we want? */
+	k->ipktinfo[r_slot].rtr = 0;	/* Yes */
+    } else {
+        freerslot(k,r_slot);		/* No, discard it. */
+
+        if (seq == prev) {              /* If it's the previous packet again */
+	    debug(DB_LOG,"PREVIOUS PKT RETRIES",0,
+		  (long)(k->ipktinfo[r_slot].rtr));
+            if (k->ipktinfo[r_slot].rtr++ > k->retry) { /* Count retries */
+                epkt("Too many retries", k); /* Too may */
+                return(X_ERROR);	/* Give up */
+            } else {			/* Otherwise */
+		return(resend(k));	/* Send old outbound packet buffer */
+            }
+#ifdef RECVONLY
+	} else {
+	    return(nak(k,k->r_seq,r_slot));
+#else
+        } else if (k->what == W_RECV) {	/* Otherwise NAK the one we want */
+	    return(nak(k,k->r_seq,r_slot));
+	} else {			/* or whatever... */
+	    return(resend(k));
+#endif /* RECVONLY */
+	} 
+    }
+#ifndef RECVONLY
+    if (k->what == W_SEND) {		/* Sending, check for ACK */
+	if (t != 'Y') {			/* Not an ACK */
+	    debug(DB_LOG,"t!=Y t",0,t);
+	    freerslot(k,r_slot);	/* added 2004-06-30 -- JHD */
+	    return(resend(k));
+	}
+	if (k->state == S_DATA) {	/* ACK to Data packet?*/
+	    if (k->cancel ||		/* Cancellation requested by caller? */
+		*p == 'X' || *p == 'Z') { /* Or by receiver? */
+		k->closef(k,*p,1);	  /* Close input file*/
+		nxtpkt(k);		  /* Next packet sequence number */
+		if ((rc = spkt('Z',k->s_seq,0,(UCHAR *)0,k)) != X_OK)
+		  return(rc);
+		if (*p == '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->status = S_EOF;
+		k->r_seq = k->s_seq;	/* Sequence number of packet we want */
+		return(X_OK);
+	    }
+	}
+	freerslot(k,r_slot);		/* It is, free the ACK. */
+    }
+#endif /* RECVONLY */
+
+/* Now we have an incoming packet with the expected sequence number. */
+
+    debug(DB_CHR,"Packet OK",0,t);
+    debug(DB_LOG,"State",0,k->state);
+
+    switch (k->state) {                 /* Kermit protocol state switcher */ 
+
+#ifndef RECVONLY
+      case S_INIT:			/* Got other Kermit's parameters */
+      case S_EOF:			/* Got ACK to EOF packet */
+	nxtpkt(k);			/* Get next packet number etc */
+	if (k->state == S_INIT) {	/* Got ACK to S packet? */
+	    spar(k,p,datalen);		/* Set negotiated parameters */
+	    debug(DB_CHR,"Parity",0,k->parity);
+	    debug(DB_LOG,"Ebqflg",0,(k->ebqflg));
+	    debug(DB_CHR,"Ebq",0,(k->ebq));
+	}
+	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 */
+	      return(rc);
+	    encstr(k->filename,k,r);	/* Encode the name for transmission */
+	    if ((rc = spkt('F',k->s_seq,-1,k->xdata,k)) != X_OK)
+	      return(rc);		/* Send F packet */
+	    r->sofar = 0L;
+	    k->state = S_FILE;		/* Wait for ACK */
+	    r->status = S_FILE;
+	} else {			/* No more files - we're done */
+	    if ((rc = spkt('B',k->s_seq,0,(UCHAR *)0,k)) != X_OK)
+	      return(rc);		/* Send EOT packet */
+	    k->state = S_EOT;		/* Wait for ACK */
+	    r->status = 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 */
+#ifdef F_AT
+	if (k->capas & CAP_AT) {	/* A-packets negotiated? */
+	    if ((rc = sattr(k,r)) != X_OK) /* Yes, send Attribute packet */
+	      return(rc);
+	    k->state = S_ATTR;		/* And wait for its ACK */
+	    r->status = S_ATTR;
+	} else
+#endif /* F_AT */
+	  if (sdata(k,r) == 0) {	/* No A packets - send first data */
+	    /* File is empty so send EOF packet */
+	    if ((rc = spkt('Z',k->s_seq,0,(UCHAR *)0,k)) != X_OK)
+	      return(rc);
+	    k->closef(k,*p,1);		/* Close input file*/
+	    k->state = S_EOF;		/* Wait for ACK to EOF */
+	    r->status = S_EOF;
+	} else {			/* Sent some data */
+	    k->state = S_DATA;		/* Wait for ACK to first data */
+	    r->status = S_DATA;
+	}
+	k->r_seq = k->s_seq;		/* Sequence number to wait for */
+	return(X_OK);
+
+      case S_ATTR:			/* Got ACK to A packet */
+      case S_DATA:			/* Got ACK to D packet */
+	nxtpkt(k);			/* Get next packet number */
+	if (k->state == S_ATTR) {
+	    /* CHECK ATTRIBUTE RESPONSE */
+	    /* IF REJECTED do the right thing... */
+	    k->state = S_DATA;
+	    r->status = S_DATA;
+	}
+	rc = sdata(k,r);		/* Send first or next data packet */
+
+	debug(DB_LOG,"Seq",0,(k->s_seq));
+	debug(DB_LOG,"sdata()",0,rc);
+
+	if (rc == 0) {			/* If there was no data to send */
+	    if ((rc = spkt('Z',k->s_seq,0,(UCHAR *)0,k)) != X_OK)
+	      return(rc);		/* Send EOF */
+	    k->closef(k,*p,1);		/* Close input file*/
+	    k->state = S_EOF;		/* And wait for ACK */
+	    r->status = S_EOF;
+	}				/* Otherwise stay in data state */
+	k->r_seq = k->s_seq;		/* Sequence number to wait for */
+	return(X_OK);
+
+      case S_EOT:			/* Get ACK to EOT packet */
+        return(X_DONE);			/* (or X_ERROR) */
+#endif /* RECVONLY */
+
+      case R_WAIT:                      /* Waiting for the S packet */
+        if (t == 'S') {                 /* Got it */
+            spar(k,p,datalen);          /* Set parameters from it */
+            rc = rpar(k,'Y');		/* ACK with my parameters */
+	    debug(DB_LOG,"rpar rc",0,rc);
+            if (rc != X_OK)
+              return(X_ERROR);          /* I/O error, quit. */
+            k->state = R_FILE;          /* All OK, switch states */
+            r->status = R_FILE;
+        } else {                        /* Wrong kind of packet, send NAK */
+            epkt("Unexpected packet type",k);
+            rc = X_ERROR;
+        }
+        freerslot(k,r_slot);		/* Free packet slot */
+        return(rc);
+
+      case R_FILE:                      /* Want an F or B packet */
+        if (t == 'F') {                 /* File name */
+            if ((rc = decode(k, r, 0, p)) == X_OK) /* Decode and save */
+              k->state = R_ATTR;        /* Switch to next state */
+	    r->status = 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 */
+		rc = ack(k, k->r_seq, r->filename); /* so ACK the F packet */
+	    } else {
+		epkt("Filename error",k); /* Error decoding filename */
+	    }
+        } else if (t == 'B') {          /* Break, end of transaction */
+            freerslot(k,r_slot);
+            rc = (ack(k, k->r_seq, (UCHAR *)0) == X_OK) ? X_DONE : X_ERROR;
+
+        } else {
+            epkt("Unexpected packet type",k);
+            rc = X_ERROR;
+        }
+        freerslot(k,r_slot);
+        return(rc);
+
+      case R_ATTR:                      /* Want A, D, or Z packet */
+#ifdef F_AT
+        if (t == 'A') {                 /* Attribute packet */
+	    int x;
+            x = gattr(k, p, r);		/* Read the attributes */
+	    if (x > -1)
+	      k->binary = x;
+            freerslot(k,r_slot);
+            ack(k, k->r_seq, (UCHAR *) "Y"); /* Always accept the file */
+            return(X_OK);
+        } else
+#endif /* F_AT */
+	  if (t == 'D') {		/* First data packet */
+            k->obufpos = 0;             /* Initialize output buffer */
+	    k->filename = r->filename;
+	    r->sofar = 0L;
+            if ((rc = (*(k->openf))(k,r->filename, 2)) == X_OK) {
+                k->state = R_DATA;      /* Switch to Data state */
+		r->status = k->state;
+                rc = decode(k, r, 1, p); /* Write out first data packet */
+                freerslot(k,r_slot);
+            } else {
+                epkt("File refused or can't be opened", k);
+                freerslot(k,r_slot);
+                return(rc);
+            }
+            if (rc == X_OK)
+              rc = ack(k, k->r_seq, s);
+            else
+              epkt("Error writing data", k);
+            return(rc);
+	} else if (t == '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 */
+            if ((rc = (*(k->openf))(k,r->filename, 2)) == X_OK) {
+		if (((rc = (*(k->closef))(k,*p,2)) == X_OK)) {
+		    k->state = R_FILE;
+		    rc = ack(k, k->r_seq, s);
+		} else {
+		    epkt("Error closing empty file", k);
+		    freerslot(k,r_slot);
+		    return(rc);
+		}
+            } else {
+                epkt("File refused or can't be opened", k);
+                freerslot(k,r_slot);
+                return(rc);
+            }
+	    r->status = k->state;
+            freerslot(k,r_slot);
+
+        } else {
+            epkt("Unexpected packet type",k);
+            return(X_ERROR);
+        }
+        break;
+
+      case R_DATA:                      /* Want a D or Z packet */
+	debug(DB_CHR,"R_DATA t",0,t);
+        if (t == 'D') {                 /* Data */
+            rc = decode(k, r, 1, p);	/* Decode it */
+            freerslot(k,r_slot);
+        } else if (t == 'Z') {          /* End of file */
+	    debug(DB_CHR,"R_DATA",0,t);
+            if (k->obufpos > 0) {       /* Flush output buffer */
+                rc = (*(k->writef))(k,k->obuf,k->obufpos);
+		debug(DB_LOG,"R_DATA writef rc",0,rc);
+		r->sofar += k->obufpos;
+                k->obufpos = 0;
+            }
+            if (((rc = (*(k->closef))(k,*p,2)) == X_OK) && (rc == X_OK))
+              k->state = R_FILE;
+	    debug(DB_LOG,"R_DATA closef rc",0,rc);
+	    r->status = k->state;
+            freerslot(k,r_slot);
+        } else {
+            epkt("Unexpected packet type",k);
+            return(X_ERROR);
+        }
+        if (rc == X_OK)
+          rc = ack(k, k->r_seq, s);
+        else
+          epkt(t == 'Z' ? "Can't close file" : "Error writing data",k);
+        return(rc);
+
+      case R_ERROR:                     /* Canceled from above */
+      default:
+        epkt(msg,k);
+        return(X_ERROR);
+    }
+    return(X_ERROR);
+}
+
+/* Utility routines */
+
+UCHAR *
+getrslot(struct k_data *k, short *n) {   /* Find a free packet buffer */
+    register int i;
+/*
+  Note: We don't clear the retry count here.
+  It is cleared only after the NEXT packet arrives, which
+  indicates that the other Kermit got our ACK for THIS packet.
+*/
+    for (i = 0; i < P_WSLOTS; i++) {    /* Search */
+        if (k->ipktinfo[i].len < 1) {
+            *n = i;                     /* Slot number */
+            k->ipktinfo[i].len = -1;	/* Mark it as allocated but not used */
+            k->ipktinfo[i].seq = -1;
+            k->ipktinfo[i].typ = SP;
+            /* k->ipktinfo[i].rtr =  0; */  /* (see comment above) */
+            k->ipktinfo[i].dat = (UCHAR *)0;
+            return(&(k->ipktbuf[0][i]));
+        }
+    }   
+    *n = -1;
+    return((UCHAR *)0);
+}
+
+void					/* Initialize a window slot */
+freerslot(struct k_data *k, short n) {
+    k->ipktinfo[n].len = 0;		/* Packet length */
+#ifdef COMMENT
+    k->ipktinfo[n].seq = 0;		/* Sequence number */
+    k->ipktinfo[n].typ = (char)0;	/* Type */
+    k->ipktinfo[n].rtr = 0;		/* Retry count */
+    k->ipktinfo[n].flg = 0;		/* Flags */
+#endif /* COMMENT */
+}
+
+UCHAR *
+getsslot(struct k_data *k, short *n) {   /* Find a free packet buffer */
+#ifdef COMMENT
+    register int i;
+    for (i = 0; i < P_WSLOTS; i++) {    /* Search */
+        if (k->opktinfo[i].len < 1) {
+            *n = i;                     /* Slot number */
+            k->opktinfo[i].len = -1;	/* Mark it as allocated but not used */
+            k->opktinfo[i].seq = -1;
+            k->opktinfo[i].typ = SP;
+            k->opktinfo[i].rtr =  0;
+            k->opktinfo[i].dat = (UCHAR *)0;
+            return(&(k->opktbuf[0][i]));
+        }
+    }   
+    *n = -1;
+    return((UCHAR *)0);
+#else
+    *n = 0;
+    return(k->opktbuf);
+#endif /* COMMENT */
+}
+
+void                                    /* Initialize a window slot */
+freesslot(struct k_data * k, short n) {
+    k->opktinfo[n].len = 0;		/* Packet length */
+    k->opktinfo[n].seq = 0;		/* Sequence number */
+    k->opktinfo[n].typ = (char)0;	/* Type */
+    k->opktinfo[n].rtr = 0;		/* Retry count */
+    k->opktinfo[n].flg = 0;		/* Flags */
+}
+
+/*  C H K 1  --  Compute a type-1 Kermit 6-bit checksum.  */
+
+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);
+}
+
+#ifdef F_CRC
+
+/*  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++) {
+#ifdef COMMENT
+        c = crc ^ (long)(*pkt);
+        crc = (crc >> 8) ^ (k->crcta[(c & 0xF0) >> 4] ^ k->crctb[c & 0x0F]);
+#else
+	c = crc ^ (*pkt);
+       crc = (crc >> 8) ^ ((k->crcta[(c & 0xF0) >> 4]) ^ (k->crctb[c & 0x0F]));
+#endif	/*  COMMENT */
+    }
+    return(crc);
+}
+#endif /* F_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) {
+
+    unsigned int crc;                   /* For building CRC */
+    int i, j, lenpos, m, n, x;		/* Workers */
+    UCHAR * s, * buf;
+
+    debug(DB_LOG,"spkt len 1",0,len);
+    if (len < 0) {			/* Calculate data length ourselves? */
+	len = 0;
+	s = data;
+	while (*s++) len++;
+    }
+    debug(DB_LOG,"spkt len 2",0,len);
+    buf = k->opktbuf;			/* Where to put packet (FOR NOW) */
+
+    i = 0;                              /* Packet buffer position */
+    buf[i++] = k->s_soh;		/* SOH */
+    lenpos = i++;			/* Remember this place */
+    buf[i++] = tochar(seq);		/* Sequence number */
+    buf[i++] = typ;			/* Packet type */
+    j = len + k->bct;
+#ifdef F_LP
+    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 */
+#endif /* F_LP */
+	buf[lenpos] = tochar(j+2);	/* Single-byte length in LEN field */
+#ifdef F_LP
+    }
+#endif /* F_LP */
+    if (data)                           /* Copy data, if any */
+      for ( ; len--; i++)
+        buf[i] = *data++;
+    buf[i] = '\0';
+
+#ifdef F_CRC
+    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);
+#ifdef XAC
+	/* HiTech's XAC compiler silently ruins the regular 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);
+	}
+#else
+        buf[i++] = (unsigned)tochar((j >> 6) & 077);
+        buf[i++] = (unsigned)tochar(j & 077);
+#endif /* XAC */
+        break;
+      case 3:                           /* 3 = 16-bit CRC */
+        crc = chk3(&buf[lenpos],k);
+#ifdef XAC
+	/* 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);
+	}
+#else
+        buf[i++] = (unsigned)tochar(((crc & 0170000)) >> 12);
+        buf[i++] = (unsigned)tochar((crc >> 6) & 077);
+        buf[i++] = (unsigned)tochar(crc & 077);
+#endif /* XAC */
+        break;
+    }
+#else
+    buf[i++] = tochar(chk1(&buf[lenpos],k));
+#endif /* F_CRC */
+
+    buf[i++] = k->s_eom;		/* Packet terminator */
+    buf[i] = '\0';			/* String terminator */
+    k->s_seq = seq;                     /* Remember sequence number */
+
+    k->opktlen = i;			/* Remember length for retransmit */
+
+#ifdef DEBUG
+/* CORRUPT THE PACKET SENT BUT NOT THE ONE WE SAVE */
+    if (xerror()) {
+	UCHAR p[P_PKTLEN+8];
+	int i;
+	for (i = 0; i < P_PKTLEN; i++)
+	  if (!(p[i] = buf[i]))
+	    break;
+	p[i-2] = 'X';
+	debug(DB_PKT,"XPKT",(char *)&p[1],0);
+	return((*(k->txd))(k,p,k->opktlen)); /* Send it. */
+    }
+    debug(DB_PKT,"SPKT",(char *)&buf[1],0);
+#endif /* DEBUG */
+
+    return((*(k->txd))(k,buf,k->opktlen)); /* Send it. */
+}
+
+/*  N A K  --  Send a NAK (negative acknowledgement)  */
+
+STATIC int
+nak(struct k_data * k, short seq, short slot) {
+    int rc;
+    rc = spkt('N', seq, 0, (UCHAR *)0, k);
+    if (k->ipktinfo[slot].rtr++ > k->retry)
+      rc = X_ERROR;
+    return(rc);
+}
+
+/*  A C K  --  Send an ACK (positive acknowledgement)  */
+
+STATIC int
+ack(struct k_data * k, short seq, UCHAR * text) {
+    int len, rc;
+    len = 0;
+    if (text) {                         /* Get length of data */
+        UCHAR *p;
+        p = text;
+        for ( ; *p++; len++) ;
+    }
+    rc = spkt('Y', seq, len, text, k);  /* Send the packet */
+    debug(DB_LOG,"ack spkt rc",0,rc);
+    if (rc == X_OK)                     /* If OK */
+      k->r_seq = (k->r_seq + 1) % 64;   /* bump the packet number */
+    return(rc);
+}
+
+/*  S P A R  --  Set parameters requested by other Kermit  */
+
+STATIC void
+spar(struct k_data * k, UCHAR *s, int datalen) {
+    int x, y;
+    UCHAR c;
+
+    s--;                                /* Line up with field numbers. */
+
+    if (datalen >= 1)                   /* Max packet length to send */
+      k->s_maxlen = xunchar(s[1]);
+
+    if (datalen >= 2)                   /* Timeout on inbound packets */
+      k->r_timo = xunchar(s[2]);
+
+    /* No padding */
+
+    if (datalen >= 5)                   /* Outbound Packet Terminator */
+      k->s_eom = xunchar(s[5]);
+
+    if (datalen >= 6)                   /* Incoming control prefix */
+      k->r_ctlq = s[6];
+
+    if (datalen >= 7) {                 /* 8th bit prefix */
+        k->ebq = s[7];
+        if ((s[7] > 32 && s[7] < 63) || (s[7] > 95 && s[7] < 127)) {
+	    if (!k->parity)		/* They want it */
+	      k->parity = 1;		/* Set parity to something nonzero */
+	    k->ebqflg = 1;
+	} else if (s[7] == 'Y' && k->parity) {
+	    k->ebqflg = 1;
+	    k->ebq = '&';
+	} else if (s[7] == 'N') {
+	    /* WHAT? */
+	}
+    }
+    if (datalen >= 8) {                 /* Block check */
+        k->bct = s[8] - '0';
+#ifdef F_CRC
+        if ((k->bct < 1) || (k->bct > 3))
+#endif /* F_CRC */
+	  k->bct = 1;
+	if (k->bctf) 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]);
+
+#ifdef F_LP                             /* Long packets */
+        if (!(x & CAP_LP))
+#endif /* F_LP */
+          k->capas &= ~CAP_LP;
+
+#ifdef F_SW                             /* Sliding Windows */
+        if (!(x & CAP_SW))
+#endif /* F_SW */
+          k->capas &= ~CAP_SW;
+
+#ifdef F_AT                             /* Attributes */
+        if (!(x & CAP_AT))
+#endif /* F_AT */
+          k->capas &= ~CAP_AT;
+
+#ifdef F_RS                             /* Recovery */
+        if (!(x & CAP_RS))
+#endif /* F_RS */
+          k->capas &= ~CAP_RS;
+
+#ifdef F_LS                             /* Locking shifts */
+        if (!(x & CAP_LS))
+#endif /* F_LS */
+          k->capas &= ~CAP_LS;
+
+        /* In case other Kermit sends addt'l capas fields ... */
+
+        for (y = 10; (xunchar(s[y]) & 1) && (datalen >= y); y++) ;
+    }
+
+#ifdef F_LP                             /* Long Packets */
+    if (k->capas & CAP_LP) {
+        if (datalen > y+1) {
+            x = xunchar(s[y+2]) * 95 + xunchar(s[y+3]);
+            k->s_maxlen = (x > P_PKTLEN) ? P_PKTLEN : x;
+            if (k->s_maxlen < 10)
+              k->s_maxlen = 60;
+        }
+    }
+#endif /* F_LP */
+    
+    debug(DB_LOG,"S_MAXLEN",0,k->s_maxlen);
+
+#ifdef F_SW
+    if (k->capas & CAP_SW) {
+        if (datalen > y) {
+            x = xunchar(s[y+1]);
+            k->window = (x > P_WSLOTS) ? P_WSLOTS : x;
+            if (k->window < 1)          /* Watch out for bad negotiation */
+              k->window = 1;
+            if (k->window > 1)
+              if (k->window > k->retry)   /* Retry limit must be greater */
+                k->retry = k->window + 1; /* than window size. */
+        }
+    }
+#endif /* F_SW */
+}
+
+/*  R P A R  --  Send my parameters to other Kermit  */
+
+STATIC int
+rpar(struct k_data * k, char type) {
+    UCHAR *d;
+    int rc, len;
+#ifdef F_CRC
+    short b;
+#endif /* F_CRC */
+
+    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->bctf)			/* Block check type */
+      d[7] = '5';			/* FORCE 3 */
+    else
+      d[7] = k->bct + '0';		/* Normal */
+    d[ 8] = k->rptq;			/* Repeat prefix */
+    d[ 9] = tochar(k->capas);           /* Capability bits */
+    d[10] = tochar(k->window);          /* Window size */
+
+#ifdef F_LP
+    d[11] = tochar(k->r_maxlen / 95);   /* Long packet size, big part */
+    d[12] = tochar(k->r_maxlen % 95);   /* Long packet size, little part */
+    d[13] = '\0';			/* Terminate the init string */
+    len = 13;
+#else
+    d[11] = '\0';
+    len = 11;
+#endif /* F_LP */
+
+#ifdef F_CRC
+    if (!(k->bctf)) {			/* Unless FORCE 3 */
+	b = k->bct;
+	k->bct = 1;			/* Always use block check type 1 */
+    }
+#endif /* F_CRC */
+    switch (type) {
+      case 'Y':				/* This is an ACK for packet 0 */
+	rc = ack(k,0,d);
+	break;
+      case 'S':				/* It's an S packet */
+	rc = spkt('S', 0, len, d, k);
+	break;
+      default:
+	rc = -1;
+    }
+#ifdef F_CRC
+    if (!(k->bctf)) {			/* Unless FORCE 3 */
+	k->bct = b;
+    }
+#endif /* F_CRC */
+    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) {
+
+    register unsigned int a, a7;        /* Current character */
+    unsigned int b8;                    /* 8th bit */
+    int rpt;                            /* Repeat count */
+    int rc;				/* Return code */
+    UCHAR *p;
+
+    rc = X_OK;
+    rpt = 0;                            /* Initialize repeat count. */
+    if (f == 0)                         /* Output function... */
+      p = r->filename;
+
+    while ((a = *inbuf++ & 0xFF) != '\0') { /* Character loop */
+        if (k->rptflg && a == k->rptq) { /* Got a repeat prefix? */
+            rpt = xunchar(*inbuf++ & 0xFF); /* Yes, get the repeat count, */
+            a = *inbuf++ & 0xFF;        /* and get the prefixed character. */
+        }
+        b8 = 0;                         /* 8th-bit value */
+        if (k->parity && (a == k->ebq)) { /* Have 8th-bit prefix? */
+            b8 = 0200;                  /* Yes, flag the 8th bit */
+            a = *inbuf++ & 0x7F;        /* and get the prefixed character. */
+        }
+        if (a == k->r_ctlq) {           /* If control prefix, */
+            a  = *inbuf++ & 0xFF;       /* get its operand */
+            a7 = a & 0x7F;              /* and its low 7 bits. */
+            if ((a7 >= 0100 && a7 <= 0137) || a7 == '?') /* Controllify */
+              a = ctl(a);               /* if in control range. */
+        }
+        a |= b8;                        /* OR in the 8th bit */
+
+        if (rpt == 0) rpt = 1;          /* If no repeats, then one */
+
+        for (; rpt > 0; rpt--) {        /* Output the char 'rpt' times */
+            if (f == 0) {
+                *p++ = (UCHAR) a;       /* to memory */
+            } else {                    /* or to file */
+                k->obuf[k->obufpos++] = (UCHAR) a; /* Deposit the byte */
+                if (k->obufpos == k->obuflen) { /* Buffer full? */
+                    rc = (*(k->writef))(k,k->obuf,k->obuflen); /* Dump it. */
+		    r->sofar += k->obuflen;
+		    if (rc != X_OK) break;
+                    k->obufpos = 0;
+                }
+            }
+        }
+    }
+    if (f == 0)                         /* If writing to memory */
+      *p = '\0';			/* terminate the string */
+    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);
+}
+
+#ifdef F_AT
+
+/*
+  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, fsizek;                 /* 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 size",sizebuf,0);
+	    }
+            break;
+
+          case '#':                     /* File creation date */
+            for (i = 0; (i < aln) && (i < DATE_MAX); i++)
+              r->filedate[i] = *s++;    /* Copy it into a static string */
+            if (i < aln) s += (aln - i);
+            r->filedate[i] = '\0';
+            break;
+
+          case '1':                     /* File length in bytes */
+            for (i = 0; (i < aln) && (i < SIZEBUFL); i++) /* Copy it */
+              sizebuf[i] = *s++;
+            sizebuf[i] = '\0';           /* Terminate with null */
+            if (i < aln) s += (aln - i);
+            fsize = stringnum(sizebuf,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;
+    short tmp;
+    long filelength;
+    UCHAR datebuf[DATE_MAX], * p;
+
+    debug(DB_LOG,"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,"sattr filename: ",k->filename,0);
+    debug(DB_LOG,"sattr filedate: ",datebuf,0);
+    debug(DB_LOG,"sattr filelength",0,filelength);
+    debug(DB_LOG,"sattr 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_LOG,"sattr k->xdata: ",k->xdata,0);
+    return(spkt('A',k->s_seq,-1,k->xdata,k));
+}
+#endif /* F_AT */
+
+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 */
+
+    debug(DB_LOG,"getpkt k->s_first",0,(k->s_first));
+    debug(DB_LOG,"getpkt k->s_remain=",k->s_remain,0);
+
+    maxlen = k->s_maxlen - k->bct - 3;	/* 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 DEBUG
+	    k->zincnt = -1234;
+	    k->dummy = 0;
+#endif /* DEBUG */
+	    c = zgetc();
+
+#ifdef DEBUG
+	    if (k->dummy) debug(DB_LOG,"DUMMY CLOBBERED (A)",0,0);
+#endif /* DEBUG */
+	}
+	if (c < 0) {			/* Watch out for empty file. */
+	    debug(DB_CHR,"getpkt first c",0,c);
+	    k->s_first = -1;
+	    return(k->size = 0);
+	}
+	r->sofar++;
+	debug(DB_LOG,"getpkt first c",0,c);
+    } else if (k->s_first == -1 && !k->s_remain[0]) { /* EOF from last time? */
+        return(k->size = 0);
+    }
+    for (k->size = 0;
+	 (k->xdata[k->size] = k->s_remain[k->size]) != '\0';
+	 (k->size)++)
+      ;
+    k->s_remain[0] = '\0';
+    if (k->s_first == -1)
+      return(k->size);
+
+    rpt = 0;				/* Initialize repeat counter. */
+    while (k->s_first > -1) {		/* Until end of file or string... */
+	if (k->istring) {
+	    next = *(k->istring)++;
+	    if (!next) next = -1;
+	} else {
+#ifdef DEBUG
+	    k->dummy = 0;
+#endif /* DEBUG */
+	    next = zgetc();
+#ifdef DEBUG
+	    if (k->dummy) debug(DB_LOG,"DUMMY CLOBBERED B",0,k->dummy);
+#endif /* DEBUG */
+	}
+	if (next < 0) {			/* If none, we're at EOF. */
+	    k->s_first = -1;
+	} else {			/* Otherwise */
+	    r->sofar++;			/* 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. */
+
+        if (k->size == maxlen)		/* Just at end, done. */
+	  return(k->size);
+
+        if (k->size > maxlen) {		/* Past end, must save some. */
+            for (i = 0;
+		 (k->s_remain[i] = k->xdata[(k->osize)+i]) != '\0';
+		 i++)
+	      ;
+            k->size = k->osize;
+            k->xdata[k->size] = '\0';
+            return(k->size);		/* Return size. */
+        }
+    }
+    return(k->size);			/* EOF, return size. */
+}
+
+#ifndef RECVONLY
+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",0,len);
+    if (len < 1)
+      return(0);
+    rc = spkt('D',k->s_seq,len,k->xdata,k); /* Send the packet */
+    debug(DB_LOG,"sdata spkt",0,rc);
+    return((rc == X_ERROR) ? rc : len);
+}
+#endif /* RECVONLY */
+
+/*  E P K T  --  Send a (fatal) Error packet with the given message  */
+
+STATIC void
+epkt(char * msg, struct k_data * k) {
+    if (!(k->bctf)) {			/* Unless FORCE 3 */
+	k->bct = 1;
+    }
+    (void) spkt('E', 0, -1, (UCHAR *) msg, k);
+}
+
+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 */
+
+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);
+    *(k->ostring) = '\0';		/* Terminate with null */
+    k->ostring = (UCHAR *)0;		/* Reset output string pointer */
+}
+
+STATIC void
+encode(int a, int next, struct k_data * k) { /* Encode character into packet */
+    int a7, b8, maxlen;
+
+    maxlen = k->s_maxlen - 4;
+    if (k->rptflg) {			/* Doing run-length encoding? */
+	if (a == next) {		/* Yes, got a run? */
+	    if (++(k->s_rpt) < 94) {	/* Yes, count. */
+		return;
+	    } else if (k->s_rpt == 94) { /* If at maximum */
+	    	k->xdata[(k->size)++] = k->rptq; /* Emit prefix, */
+		k->xdata[(k->size)++] = tochar(k->s_rpt); /* and count, */
+		k->s_rpt = 0;		/* and reset counter. */
+	    }
+	} else if (k->s_rpt == 1) {	/* Run broken, only two? */
+	    k->s_rpt = 0;		/* Yes, do the character twice */
+	    encode(a,-1,k);		/* by calling self recursively. */
+	    if (k->size <= maxlen)	/* Watch boundary. */
+	      k->osize = k->size;
+	    k->s_rpt = 0;		/* Call self second time. */
+	    encode(a,-1,k);
+	    return;
+	} else if (k->s_rpt > 1) {	/* Run broken, more than two? */
+	    k->xdata[(k->size)++] = k->rptq; /* Yes, emit prefix and count */
+	    k->xdata[(k->size)++] = tochar(++(k->s_rpt));
+	    k->s_rpt = 0;		/* and reset counter. */
+	}
+    }
+    a7 = a & 127;			/* Get low 7 bits of character */
+    b8 = a & 128;			/* And "parity" bit */
+
+    if (k->ebqflg && b8) {		/* If doing 8th bit prefixing */
+	k->xdata[(k->size)++] = k->ebq; /* and 8th bit on, insert prefix */
+	a = a7;				/* and clear the 8th bit. */
+    }
+    if (a7 < 32 || a7 == 127) {		   /* If in control range */
+        k->xdata[(k->size)++] = k->s_ctlq; /* insert control prefix */
+        a = ctl(a);			 /* and make character printable. */
+    } else if (a7 == k->s_ctlq)		 /* If data is control prefix, */
+      k->xdata[(k->size)++] = k->s_ctlq; /* prefix it. */
+    else if (k->ebqflg && a7 == k->ebq)  /* If doing 8th-bit prefixing, */
+      k->xdata[(k->size)++] = k->s_ctlq; /* ditto for 8th-bit prefix. */
+    else if (k->rptflg && a7 == k->rptq) /* If doing run-length encoding, */
+      k->xdata[(k->size)++] = k->s_ctlq; /* ditto for repeat prefix. */
+
+    k->xdata[(k->size)++] = a;		/* Finally, emit the character. */
+    k->xdata[(k->size)] = '\0';		/* Terminate string with null. */
+}
+
+STATIC int
+nxtpkt(struct k_data * k) {		/* Get next packet to send */
+    k->s_seq = (k->s_seq + 1) & 63;	/* Next sequence number */
+    k->xdata = k->xdatabuf;
+    return(0);
+}
+
+STATIC int
+resend(struct k_data * k) {
+    UCHAR * buf;
+    if (!k->opktlen)			/* Nothing to resend */
+      return(X_OK);
+    buf = k->opktbuf;
+    debug(DB_PKT,">PKT",&buf[1],k->opktlen);
+    return((*(k->txd))(k,buf,k->opktlen));
+}
diff --git a/kermit.c.diff b/kermit.c.diff
new file mode 100644
index 0000000..ec01213
--- /dev/null
+++ b/kermit.c.diff
@@ -0,0 +1,139 @@
+*** ../../ek16/kermit.c	2011-03-30 12:40:09.705176000 -0400
+--- kermit.c	2011-06-06 16:24:13.034202000 -0400
+***************
+*** 1,8 ****
+  #define KERMIT_C
+  /*
+    Embedded Kermit protocol module
+!   Version: 1.6
+!   Most Recent Update: Wed Mar 30 12:39:11 2011
+  
+    No stdio or other runtime library calls, no system calls, no system 
+    includes, no static data, and no global variables in this module.
+--- 1,8 ----
+  #define KERMIT_C
+  /*
+    Embedded Kermit protocol module
+!   Version: 1.7
+!   Most Recent Update: Mon Jun  6 15:36:26 2011
+  
+    No stdio or other runtime library calls, no system calls, no system 
+    includes, no static data, and no global variables in this module.
+***************
+*** 98,104 ****
+  
+      int i, j, rc;			/* Workers */
+      int datalen;                        /* Length of packet data field */
+-     int bctu;				/* Block check type for this packet */
+      UCHAR *p;                           /* Pointer to packet data field */
+      UCHAR *q;                           /* Pointer to data to be checked */
+      UCHAR *s;				/* Worker string pointer */
+--- 98,103 ----
+***************
+*** 313,319 ****
+          }
+  	debug(DB_MSG,"HDR CHKSUM OK",0,0);
+          p[2] = c;                       /* Put checksum back */
+!         datalen = xunchar(p[0])*95 + xunchar(p[1]) - k->bct; /* Data length */
+          p += 3;                         /* Fix data pointer */
+          k->ipktinfo[r_slot].dat = p;	/* Permanent record of data pointer */
+      } else {                            /* Regular packet */
+--- 312,319 ----
+          }
+  	debug(DB_MSG,"HDR CHKSUM OK",0,0);
+          p[2] = c;                       /* Put checksum back */
+! 	/* Data length */
+!         datalen = xunchar(p[0])*95 + xunchar(p[1]) - ((k->bctf) ? 3 : k->bct);
+          p += 3;                         /* Fix data pointer */
+          k->ipktinfo[r_slot].dat = p;	/* Permanent record of data pointer */
+      } else {                            /* Regular packet */
+***************
+*** 323,334 ****
+--- 323,342 ----
+      }
+  #endif /* F_LP */
+  #ifdef F_CRC
++     if (k->bctf) {			/* FORCE 3 */
++ 	chklen = 3;
++     } else {
+  	if (t == 'S' || k->state == S_INIT) { /* S-packet was retransmitted? */
++ 	    if (q[10] == '5') {		/* Block check type requested is 5 */
++ 		k->bctf = 1;		/* FORCE 3 */
++ 		chklen = 3;
++ 	    }
+  	    chklen = 1;			/* Block check is always type 1 */
+  	    datalen = k->ipktinfo[r_slot].len - 3; /* Data length */
+  	} else {
+  	    chklen = k->bct;
+  	}
++     }
+  #else
+      chklen = 1;				/* Block check is always type 1 */
+      datalen = k->ipktinfo[r_slot].len - 3; /* Data length */
+***************
+*** 1031,1036 ****
+--- 1039,1045 ----
+          if ((k->bct < 1) || (k->bct > 3))
+  #endif /* F_CRC */
+  	  k->bct = 1;
++ 	if (k->bctf) k->bct = 3;
+      }
+      if (datalen >= 9) {                 /* Repeat counts */
+          if ((s[9] > 32 && s[9] < 63) || (s[9] > 95 && s[9] < 127)) {
+***************
+*** 1120,1126 ****
+        d[ 6] = k->ebq = '&';           /* I need to request it */
+      else                                /* else just agree with other Kermit */
+        d[ 6] = k->ebq;
+!     d[ 7] = k->bct + '0';               /* Block check type */
+      d[ 8] = k->rptq;			/* Repeat prefix */
+      d[ 9] = tochar(k->capas);           /* Capability bits */
+      d[10] = tochar(k->window);          /* Window size */
+--- 1129,1138 ----
+        d[ 6] = k->ebq = '&';           /* I need to request it */
+      else                                /* else just agree with other Kermit */
+        d[ 6] = k->ebq;
+!     if (k->bctf)			/* Block check type */
+!       d[7] = '5';			/* FORCE 3 */
+!     else
+!       d[7] = k->bct + '0';		/* Normal */
+      d[ 8] = k->rptq;			/* Repeat prefix */
+      d[ 9] = tochar(k->capas);           /* Capability bits */
+      d[10] = tochar(k->window);          /* Window size */
+***************
+*** 1136,1143 ****
+--- 1148,1157 ----
+  #endif /* F_LP */
+  
+  #ifdef F_CRC
++     if (!(k->bctf)) {			/* Unless FORCE 3 */
+  	b = k->bct;
+  	k->bct = 1;			/* Always use block check type 1 */
++     }
+  #endif /* F_CRC */
+      switch (type) {
+        case 'Y':				/* This is an ACK for packet 0 */
+***************
+*** 1150,1156 ****
+--- 1164,1172 ----
+  	rc = -1;
+      }
+  #ifdef F_CRC
++     if (!(k->bctf)) {			/* Unless FORCE 3 */
+  	k->bct = b;
++     }
+  #endif /* F_CRC */
+      return(rc);                         /* Pass along return code. */
+  }
+***************
+*** 1510,1516 ****
+--- 1526,1534 ----
+  
+  STATIC void
+  epkt(char * msg, struct k_data * k) {
++     if (!(k->bctf)) {			/* Unless FORCE 3 */
+  	k->bct = 1;
++     }
+      (void) spkt('E', 0, -1, (UCHAR *) msg, k);
+  }
+  
diff --git a/kermit.h b/kermit.h
new file mode 100644
index 0000000..acfd99c
--- /dev/null
+++ b/kermit.h
@@ -0,0 +1,423 @@
+#ifndef __KERMIT_H__
+#define __KERMIT_H__
+
+#define VERSION "1.7"			/* Kermit module version number */
+
+/*
+  kermit.h -- Symbol and struct definitions for embedded Kermit.
+
+  As of version 1.6 of 30 March 2011, E-Kermit is Open Source software under
+  the Revised 3-Clause BSD license which follows.  E-Kermit 1.6 is identical
+  to version 1.51 except for the new license.
+
+  Author: Frank da Cruz.
+
+  Copyright (C) 1995, 2011, 
+  Trustees of Columbia University in the City of New York.
+  All rights reserved.
+
+  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.
+*/
+
+#ifdef COMMENT                          /* COMMENT must not be defined */
+#undef COMMENT				/* (e.g. in a platform header file) */
+#endif /* COMMENT */
+
+/*
+  Never use NULL as a pointer.  Always use 0 cast to the appropriate type,
+  for example: (UCHAR *)0.  Reason: compiler might define NULL to be an
+  integer 0, and when passed to a function that wants a pointer, might wind
+  up with junk in the left half (if pointers wider than ints).
+*/
+#ifdef NULL
+#undef NULL
+#endif /* NULL */
+
+/* Feature Selection */
+
+/* XAC compiler for Philips XAG30 microprocessor */
+/* See http://www.columbia.edu/kermit/em-apex.html */
+
+#ifdef XAC				/* XAC has tiny command line */
+#define NO_LP				/* Long packets too big for APF9 */
+#define NO_SSW
+#define NO_SCAN				/* No file system */
+#define FN_MAX  16
+#define IBUFLEN 128 
+#define OBUFLEN 512
+
+#else  /* XAC */
+
+#ifdef MINSIZE
+#define NO_LP
+#define NO_AT
+#define NO_CTRLC
+#define NO_SSW
+#define NO_CRC
+#define NO_SCAN
+#endif	/* MINSIZE */
+
+#endif	/* XAC */
+
+#ifndef NO_LP
+#define F_LP                            /* Long packets */
+#endif	/* NO_LP */
+
+#ifndef NO_AT
+#define F_AT                            /* Attribute packets */
+#endif	/* NO_AT */
+
+#ifndef NO_CTRLC
+#define F_CTRLC                         /* 3 consecutive Ctrl-C's to quit */
+#endif	/* NO_CTRLC */
+
+#ifndef NO_SSW
+#define F_SSW				/* Simulated sliding windows */
+#endif	/* NO_SSW */
+
+#ifndef NO_SCAN
+#define F_SCAN				/* Scan files for text/binary */
+#endif	/* NO_SCAN */
+
+#ifndef NO_CRC				/* Type 2 and 3 block checks */
+#define F_CRC
+#endif /* NO_CRC */
+
+/*
+  F_SSW means we say (in negotiations) that we support sliding windows, but we
+  really don't.  This allows the sender to send to us in a steady stream, and
+  works just fine except that error recovery is via go-back-to-n rather than
+  selective repeat.
+*/
+
+#ifdef COMMENT                          /* None of the following ... */
+/*
+  + = It works if selected
+  - = Partially implemented but doesn't work
+  0 = Not implemented
+*/
+  #define F_TSW                         /* - True sliding windows */
+  #define F_LS                          /* 0 Locking shifts */
+  #define F_RS                          /* 0 Recovery */
+
+#endif /* COMMENT */
+
+#ifdef F_TSW				/* F_SW is defined if either */
+#ifndef F_SW				/* F_SSW or F_TSW is defined... */
+#define F_SW
+#endif /* F_SW */
+#endif /* F_TSW */
+
+#ifdef F_SSW  
+#ifndef F_SW
+#define F_SW
+#endif /* F_SW */
+#endif /* F_SSW */
+
+/* Control character symbols */
+
+#define NUL  '\0'                       /* Null */
+#define SOH  001                        /* Start of header */
+#define LF   012                        /* Linefeed */
+#define CR   015                        /* Carriage Return */
+#define SO   016                        /* Shift Out */
+#define SI   017                        /* Shift In */
+#define DLE  020                        /* Datalink Escape */
+#define ESC  033                        /* Escape */
+#define XON  021                        /* XON */
+#define XOFF 023                        /* XOFF */
+#define SP   040                        /* Space */
+#define DEL  0177                       /* Delete (Rubout) */
+
+#ifndef HAVE_VERSION			/* k_data struct has version member */
+#define HAVE_VERSION			/* as of version 1.1 */
+#endif /* HAVE_VERSION */
+
+/* Main program return codes */
+
+#define SUCCESS     0
+#define FAILURE     1
+
+/* Buffer lengths (can be overridden in platform.h) */
+
+#ifndef RECVONLY
+#ifndef IBUFLEN
+#define IBUFLEN  1024			/* File input buffer size */
+#endif /* IBUFLEN */
+#endif	/* RECVONLY */
+
+#ifndef OBUFLEN
+#define OBUFLEN  1024                   /* File output buffer size */
+#endif /* OBUFLEN */
+
+#ifndef IDATALEN			/* S/I packet data max length */
+#define IDATALEN 32 
+#endif /* IDATALEN */
+
+#ifndef FN_MAX
+#define FN_MAX   1024                   /* Maximum filename length */
+#endif /* FN_MAX */
+
+#define DATE_MAX   20                   /* Max length for file date */
+
+/* Protocol parameters */
+
+#ifndef P_WSLOTS
+#ifdef F_SW                             /* Window slots */
+#ifdef F_TSW				/* True window slots */
+#define P_WSLOTS    4			/* Max is 4 */
+#else
+#define P_WSLOTS   31			/* Simulated max is 31 */
+#endif /* F_TSW */
+#else
+#define P_WSLOTS    1
+#endif /* F_SW */
+#endif /* P_WSLOTS */
+
+#ifndef P_PKTLEN			/* Kermit max packet length */
+#ifdef F_LP
+#define P_PKTLEN 4096
+#else
+#define P_PKTLEN   94
+#endif /* F_LP */
+#endif /* P_PKTLEN */
+
+/* Generic On/Off values */
+
+#define OFF         0
+#define ON          1
+
+/* File Transfer Modes */
+
+#define BINARY      0
+#define TEXT        1
+
+/* Parity values */
+
+#define PAR_NONE    0
+#define PAR_SPACE   1
+#define PAR_EVEN    2
+#define PAR_ODD     3
+#define PAR_MARK    4
+
+/* Protocol parameters */
+
+#define P_S_TIMO   40                   /* Timeout to tell other Kermit  */
+#define P_R_TIMO    5                   /* Default timeout for me to use */
+#define P_RETRY    10                   /* Per-packet retramsit limit    */
+#define P_PARITY  PAR_NONE              /* Default parity        */
+#define P_R_SOH   SOH                   /* Incoming packet start */
+#define P_S_SOH   SOH                   /* Outbound packet start */
+#define P_R_EOM    CR                   /* Incoming packet end   */
+#define P_S_EOM    CR                   /* Outbound packet end   */
+
+/* Capability bits */
+
+#define CAP_LP      2                   /* Long packet capability */
+#define CAP_SW      4                   /* Sliding windows capability */
+#define CAP_AT      8                   /* Attribute packet capability */
+#define CAP_RS     16                   /* Resend capability */
+#define CAP_LS     32                   /* Locking shift capability */
+
+/* Actions */
+
+#define A_SEND      1			/* Send file(s) */
+#define A_RECV      2			/* Receive file(s) */
+
+/* Receive protocol states */
+
+#define R_ERROR    -1                   /* Fatal protocol error */
+#define R_NONE      0                   /* Protocol not running */
+#define R_WAIT      1                   /* Waiting for S packet */
+#define R_FILE      2                   /* Waiting for F or B packet */
+#define R_ATTR      3                   /* Waiting for A or D packet */
+#define R_DATA      4                   /* Waiting for D or Z packet */
+
+/* Send protocol states */
+
+#define S_ERROR    -1                   /* Fatal protocol error */
+#define S_NONE     10                   /* Protocol not running */
+#define S_INIT     11                   /* Sent S packet */
+#define S_FILE     12                   /* Sent F packet */
+#define S_ATTR     13                   /* Sent A packet */
+#define S_DATA     14                   /* Sent D packet */
+#define S_EOF      15                   /* Sent Z packet */
+#define S_EOT      16                   /* Sent B packet */
+
+/* What I'm Doing */
+
+#define W_NOTHING   0
+#define W_SEND      1
+#define W_RECV      2
+
+/* Kermit module function codes */
+
+#define K_INIT      0                   /* Initialize */
+#define K_RUN       1                   /* Run */
+#define K_STATUS    2                   /* Request status */
+#define K_CONTINUE  3                   /* Keep going */
+#define K_QUIT      4                   /* Quit immediately */
+#define K_ERROR     5                   /* Quit with error packet, msg given */
+#define K_SEND      6			/* Begin Send sequence */
+
+/* Kermit module return codes */
+
+#define X_ERROR    -1                   /* Fatal error */
+#define X_OK        0                   /* OK, no action needed */
+#define X_FILE      1                   /* Filename received */
+#define X_DATA      2                   /* File data received */
+#define X_DONE      3                   /* Done */
+#define X_STATUS    4                   /* Status report */
+
+/* Interruption codes */
+
+#define I_FILE      1			/* Cancel file */
+#define I_GROUP     2			/* Cancel group */
+
+struct packet {
+    int len;                            /* Length */
+    short seq;                          /* Sequence number */
+    char typ;                           /* Type */
+    short rtr;                          /* Retry count */
+    UCHAR * dat;                        /* Pointer to data */
+    short flg;				/* Flags */
+};
+
+struct k_data {                         /* The Kermit data structure */
+    UCHAR * version;			/* Version number of Kermit module */
+    short remote;			/* 0 = local, 1 = remote */
+    short xfermode;			/* 0 = automatic, 1 = manual */
+    short binary;                       /* 0 = text, 1 = binary */
+    short state;                        /* Kermit protocol state */
+    short what;				/* Action (send or receive) */
+    short s_first;			/* Enocode at beginning of file */
+    short s_next;			/* Encode lookahead byte */
+    short s_seq;                        /* Sequence number sent */
+    short r_seq;                        /* Sequence number received */
+    short s_type;                       /* Packet type sent */
+    short r_type;                       /* Packet type received */
+    short s_soh;                        /* Packet start sent */
+    short r_soh;                        /* Packet start received */
+    short s_eom;                        /* Packet end sent */
+    short r_eom;                        /* Packet end received */
+    int size;				/* Current size of output pkt data */
+    int osize;				/* Previous output packet data size */
+    int r_timo;                         /* Receive and send timers */
+    int s_timo;                         /* ... */
+    int r_maxlen;                       /* maximum packet length to receive */
+    int s_maxlen;                       /* maximum packet length to send */
+    short window;                       /* maximum window slots */
+    short wslots;                       /* current window slots */
+    short parity;                       /* 0 = none, nonzero = some */
+    short retry;                        /* retry limit */
+    short cancel;			/* Cancellation */
+    short ikeep;			/* Keep incompletely received files */
+    char s_ctlq;                        /* control-prefix out */
+    char r_ctlq;                        /* control-prefix in */
+    char ebq;				/* 8-bit prefix */
+    char ebqflg;			/* 8-bit prefixing negotiated */
+    char rptq;				/* Repeat-count prefix */
+    int s_rpt;				/* Current repeat count */
+    short rptflg;                       /* flag for repeat counts negotiated */
+    short bct;                          /* Block-check type 1..3 */
+    unsigned short capas;               /* Capability bits */
+#ifdef F_CRC
+    USHORT crcta[16];			/* CRC generation table A */
+    USHORT crctb[16];			/* CRC generation table B */
+#endif /* F_CRC */
+    UCHAR s_remain[6];			 /* Send data leftovers */
+    UCHAR ipktbuf[P_PKTLEN+8][P_WSLOTS]; /* Buffers for incoming packets */
+    struct packet ipktinfo[P_WSLOTS];    /* Incoming packet info */
+#ifdef COMMENT
+    UCHAR opktbuf[P_PKTLEN+8][P_WSLOTS]; /* Buffers for outbound packets */
+#else
+    UCHAR opktbuf[P_PKTLEN+8];		/* Outbound packet buffer */
+    int opktlen;			/* Outbound packet length */
+    UCHAR xdatabuf[P_PKTLEN+2];		/* Buffer for building data field */
+#endif /* COMMENT */
+    struct packet opktinfo[P_WSLOTS];	/* Outbound packet info */
+    UCHAR * xdata;			/* Pointer to data field of outpkt */
+#ifdef F_TSW
+    short r_pw[64];			/* Packet Seq.No. to window-slot map */
+    short s_pw[64];			/* Packet Seq.No. to window-slot map */
+#endif /* F_TSW */
+    UCHAR ack_s[IDATALEN];		/* Our own init parameter string */
+    UCHAR * obuf;
+    int rx_avail;			/* Comms bytes available for reading */
+    int obuflen;                        /* Length of output file buffer */
+    int obufpos;                        /* Output file buffer position */
+    UCHAR ** filelist;			/* List of files to send */
+    UCHAR * dir;			/* Directory */
+    UCHAR * filename;			/* Name of current file */
+    UCHAR * istring;			/* Pointer to string to encode from */
+    UCHAR * ostring;			/* Pointer to string to decode to */
+    int (*rxd)(struct k_data *, UCHAR *, int);   /* Comms read function */
+    int (*txd)(struct k_data *, UCHAR *, int);   /* and comms write function */
+    int (*ixd)(struct k_data *);	         /* and comms info function */
+    int (*openf)(struct k_data *,UCHAR *,int);   /* open-file function  */
+    ULONG (*finfo)(struct k_data *,UCHAR *,UCHAR *,int,short *,short);
+    int (*readf)(struct k_data *);	         /* read-file function  */
+    int (*writef)(struct k_data *,UCHAR *, int); /* write-file function */
+    int (*closef)(struct k_data *,UCHAR,int);    /* close-file function */
+    int (*dbf)(int,UCHAR *,UCHAR *,long);  /* debug function */
+    UCHAR * zinbuf;			/* Input file buffer itself */
+    int zincnt;				/* Input buffer position */
+    int zinlen;				/* Length of input file buffer */
+    UCHAR * zinptr;			/* Pointer to input file buffer */
+    int bctf;				/* Flag to force type 3 block check */
+    int dummy;
+};
+
+struct k_response {			/* Report from Kermit */
+    short status;                       /* Current status */
+    UCHAR filename[FN_MAX];             /* Name of current file */
+    UCHAR filedate[DATE_MAX];           /* Date of file */
+    long filesize;                      /* Size of file */
+    long sofar;				/* Bytes transferred so far */
+};
+
+/* Macro definitions */
+
+#define tochar(ch)  (UCHAR)((UCHAR)((UCHAR)(ch) + SP ))
+#define xunchar(ch) (UCHAR)((UCHAR)((UCHAR)(ch) - SP ))
+#define ctl(ch)     (UCHAR)((UCHAR)((UCHAR)(ch) ^ 64 ))
+
+#ifdef COMMENT
+#define tochar(ch)  (((ch) + SP ) & 0xFF ) /* Digit to character */
+#define xunchar(ch) (((ch) - SP ) & 0xFF ) /* Character to number */
+#define ctl(ch)     (((ch) ^ 64 ) & 0xFF ) /* Controllify/uncontrollify */
+#endif /* COMMENT */
+
+/* Prototypes for kermit() functions */
+
+int kermit(short, struct k_data *, short, int, char *, struct k_response *);
+UCHAR * getrslot(struct k_data *, short *);
+UCHAR * getsslot(struct k_data *, short *);
+void freerslot(struct k_data *, short);
+void freesslot(struct k_data *, short);
+
+#endif /* __KERMIT_H__ */
diff --git a/kermit.h.diff b/kermit.h.diff
new file mode 100644
index 0000000..12aa7ca
--- /dev/null
+++ b/kermit.h.diff
@@ -0,0 +1,29 @@
+*** ../../ek16/kermit.h	2011-03-30 13:13:04.814335000 -0400
+--- kermit.h	2011-06-06 15:36:54.700435000 -0400
+***************
+*** 1,7 ****
+  #ifndef __KERMIT_H__
+  #define __KERMIT_H__
+  
+! #define VERSION "1.6"			/* Kermit module version number */
+  
+  /*
+    kermit.h -- Symbol and struct definitions for embedded Kermit.
+--- 1,7 ----
+  #ifndef __KERMIT_H__
+  #define __KERMIT_H__
+  
+! #define VERSION "1.7"			/* Kermit module version number */
+  
+  /*
+    kermit.h -- Symbol and struct definitions for embedded Kermit.
+***************
+*** 388,393 ****
+--- 388,394 ----
+      int zincnt;				/* Input buffer position */
+      int zinlen;				/* Length of input file buffer */
+      UCHAR * zinptr;			/* Pointer to input file buffer */
++     int bctf;				/* Flag to force type 3 block check */
+      int dummy;
+  };
+  
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..e56db3c
--- /dev/null
+++ b/main.c
@@ -0,0 +1,447 @@
+/*  Embedded Kermit demo, main program.  */
+
+/*
+  Author: Frank da Cruz, the Kermit Project, Columbia University, New York.
+  Copyright (C) 1995, 2011, 
+  Trustees of Columbia University in the City of New York.
+  All rights reserved.
+
+  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.
+*/
+
+/*
+  This is a demo/test framework, to be replaced by a real control program.
+  It includes a simple Unix-style command-line parser to allow setup and
+  testing of the Kermit module.  Skip past all this to where it says REAL
+  STUFF to see the real stuff.  ANSI C required.  Note: order of the 
+  following includes is important.
+*/
+#include "cdefs.h"      /* Data types for all modules */
+#include "debug.h"	/* Debugging */
+#include "platform.h"	/* Platform-specific includes and definitions */
+#include "kermit.h"	/* Kermit symbols and data structures */
+#ifdef __linux
+#include <errno.h>
+#endif /* __linux */
+
+/*
+  Sample prototypes for i/o functions.
+  The functions are defined in a platform-specific i/o module.
+  The function names are unknown to the Kermit module.
+  The names can be changed but not their calling conventions.
+  The following prototypes are keyed to unixio.c.
+*/
+int devopen(char *);			/* Communications device/path */
+int devsettings(char *);
+int devrestore(void);
+int devclose(void);
+int pktmode(short);
+
+int readpkt(struct k_data *, UCHAR *, int); /* Communications i/o functions */
+int tx_data(struct k_data *, UCHAR *, int);
+int inchk(struct k_data *);
+
+int openfile(struct k_data *, UCHAR *, int); /* File i/o functions */
+int writefile(struct k_data *, UCHAR *, int);
+int readfile(struct k_data *);
+int closefile(struct k_data *, UCHAR, int);
+ULONG fileinfo(struct k_data *, UCHAR *, UCHAR *, int, short *, short);
+
+/* External data */
+
+extern UCHAR o_buf[];                   /* Must be defined in io.c */
+extern UCHAR i_buf[];                   /* Must be defined in io.c */
+extern int errno;
+
+/* Data global to this module */
+
+struct k_data k;                        /* Kermit data structure */
+struct k_response r;                    /* Kermit response structure */
+
+char **xargv;				/* Global pointer to arg vector */
+UCHAR **cmlist = (UCHAR **)0;		/* Pointer to file list */
+char * xname = "ek";			/* Default program name */
+
+int xargc;				/* Global argument count */
+int nfils = 0;				/* Number of files in file list */
+int action = 0;				/* Send or Receive */
+int xmode = 0;				/* File-transfer mode */
+int ftype = 1;				/* Global file type 0=text 1=binary*/
+int keep = 0;				/* Keep incompletely received files */
+int db = 0;				/* Debugging */
+short fmode = -1;			/* Transfer mode for this file */
+int parity = 0;				/* Parity */
+#ifdef F_CRC
+int check = 3;				/* Block check */
+#else
+int check = 1;
+#endif /* F_CRC */
+int remote = 1;				/* 1 = Remote, 0 = Local */
+#ifdef DEBUG
+int errorrate = 0;			/* Simulated error rate */
+int seed = 1234;			/* Random number generator seed */
+#endif /* DEBUG */
+
+void
+doexit(int status) {
+    devrestore();                       /* Restore device */
+    devclose();                         /* Close device */
+    exit(status);                       /* Exit with indicated status */
+}
+
+void
+usage() {
+    fprintf(stderr,"E-Kermit %s\n",VERSION);
+    fprintf(stderr,"Usage: %s <options>\n",xname);
+    fprintf(stderr,"Options:\n");
+    fprintf(stderr," -r           Receive files\n");
+#ifndef RECVONLY
+    fprintf(stderr," -s <files>   Send files\n");
+#endif /* RECVONLY */
+    fprintf(stderr," -p [neoms]   Parity: none, even, odd, mark, space\n");
+#ifdef F_CRC
+    fprintf(stderr," -b [1235]    Block check type: 1, 2, 3, or 5\n");
+#endif /* F_CRC */
+    fprintf(stderr," -k           Keep incompletely received files\n");
+    fprintf(stderr," -B           Force binary mode\n");
+    fprintf(stderr," -T           Force text mode\n");
+    fprintf(stderr," -R           Remote mode (vs local)\n");
+    fprintf(stderr," -L           Local mode (vs remote)\n");
+#ifdef DEBUG
+    fprintf(stderr," -E <number>  Simulated error rate (0-100)\n");
+    fprintf(stderr," -d           Create debug.log\n");
+#endif /* DEBUG */
+    fprintf(stderr," -h           Help (this message)\n");
+    doexit(FAILURE);
+}
+
+void
+fatal(char *msg1, char *msg2, char *msg3) { /* Not to be called except */
+    if (msg1) {				    /* from this module */
+	fprintf(stderr,"%s: %s",xname,msg1);
+	if (msg2) fprintf(stderr,"%s",msg2);
+	if (msg3) fprintf(stderr,"%s",msg3);
+	fprintf(stderr,"\n");
+    }
+    doexit(FAILURE);
+}
+
+/* Simple user interface for testing */
+
+int
+doarg(char c) {				/* Command-line option parser */
+    int x;				/* Parses one option with its arg(s) */
+    char *xp, *s;
+    struct stat statbuf;
+
+    xp = *xargv+1;			/* Pointer for bundled args */
+    while (c) {
+#ifdef DEBUG
+	if (errorrate) seed += (int)c;
+#endif /* DEBUG) */
+	switch (c) {
+	  case 'r':			/* Receive */
+	    if (action) fatal("Conflicting actions",(char *)0,(char *)0);
+	    action = A_RECV;
+	    break;
+
+#ifndef RECVONLY
+	  case 's':			/* Send */
+	    if (action)
+	      fatal("Conflicting actions",(char *)0,(char *)0);
+	    if (*(xp+1))
+	      fatal("Invalid argument bundling after -s",(char *)0,(char *)0);
+	    nfils = 0;			/* Initialize file counter, flag */
+	    cmlist = (UCHAR **)(xargv+1); /* Remember this pointer */
+	    while (--xargc > 0) {	/* Traverse the list */
+		xargv++;
+		s = *xargv;
+#ifdef DEBUG
+		if (errorrate) seed += (int)*s;
+#endif /* DEBUG) */
+		if (**xargv == '-')
+		  break;
+		errno = 0;
+		x = stat(s,&statbuf);
+		if (x < 0)
+		  fatal("File '",s,"' not found");
+		if (access(s,4) < 0)
+		  fatal("File '",s,"' not accessible");
+		nfils++;
+	    }
+	    xargc++, *xargv--;		/* Adjust argv/argc */
+	    if (nfils < 1)
+	      fatal("Missing filename for -s",(char *)0,(char *)0);
+	    action = A_SEND;
+	    break;
+#endif /* RECVONLY */
+
+#ifdef F_CRC
+	  case 'b':			/* Block-check type */
+#endif /* F_CRC */
+#ifdef DEBUG
+	  case 'E':			/* Simulated error rate */
+#endif /* DEBUG */
+	    if (*(xp+1))
+	      fatal("Invalid argument bundling",(char *)0,(char *)0);
+	    *xargv++, xargc--;
+	    if ((xargc < 1) || (**xargv == '-'))
+	      fatal("Missing option argument",(char *)0,(char *)0);
+	    s = *xargv;
+	    while (*s) {
+		if (!isdigit(*s))
+		  fatal("Numeric argument required",(char *)0,(char *)0);
+		s++;
+	    }
+	    if (c == 'b') {
+		check = atoi(*xargv);
+		if (check < 1 || check > 5 || check == 4)
+		  fatal("Invalid block check",(char *)0,(char *)0);
+#ifdef DEBUG
+	    } else if (c == 'E') {
+		errorrate = atoi(*xargv);
+		if (errorrate > 100)
+		  fatal("Invalid error rate",(char *)0,(char *)0);
+#endif /* DEBUG */
+	    }
+	    break;
+
+	  case 'h':			/* Help */
+	  case '?':
+	    usage();
+
+	  case 'B':			/* Force binary file transfer */
+	    xmode = 1;			/* So no automatic switching */
+	    ftype = BINARY;
+	    break;
+
+	  case 'T':			/* Force text file transfer */
+	    xmode = 1;			/* So no automatic switching */
+	    ftype = TEXT;
+	    break;
+
+	  case 'R':			/* Tell Kermit it's in remote mode */
+	    remote = 1;
+	    break;
+
+	  case 'L':			/* Tell Kermit it's in local mode */
+	    remote = 0;
+	    break;
+
+	  case 'k':			/* Keep incompletely received files */
+	    keep = 1;
+	    break;
+
+	  case 'p':			/* Parity */
+	    if (*(xp+1))
+	      fatal("Invalid argument bundling",(char *)0,(char *)0);
+	    *xargv++, xargc--;
+	    if ((xargc < 1) || (**xargv == '-'))
+	      fatal("Missing parity",(char *)0,(char *)0);
+	    switch(x = **xargv) {
+	      case 'e':			/* Even */
+	      case 'o':			/* Odd */
+	      case 'm':			/* Mark */
+	      case 's': parity = x; break; /* Space */
+	      case 'n': parity = 0; break; /* None */
+	      default:  fatal("Invalid parity '", *xargv, "'");
+	    }
+	    break;
+
+#ifdef DEBUG
+	  case 'd':
+	    db++;
+	    break;
+#endif /* DEBUG */
+
+	  default:			/* Anything else */
+	    fatal("Unknown command-line option ",
+		  *xargv,
+		  " type 'ek -h' for help."
+		  );
+        }
+	c = *++xp;			/* See if options are bundled */
+    }
+    return(action);
+}
+
+void
+main(int argc, char ** argv) {
+    int status, rx_len, i, x;
+    char c;
+    UCHAR *inbuf;
+    short r_slot;
+
+    parity = P_PARITY;                  /* Set this to desired parity */
+    status = X_OK;                      /* Initial kermit status */
+
+    xargc = argc;
+    xargv = argv;
+    xname = argv[0];
+
+    while (--xargc > 0) {		/* Loop through command-line words */
+	xargv++;
+	if (**xargv == '-') {		/* Have dash */
+	    c = *(*xargv+1);		/* Get the option letter */
+	    x = doarg(c);		/* Go handle the option */
+	    if (x < 0) doexit(FAILURE);
+    	} else {			/* No dash where expected */
+	    fatal("Malformed command-line option: '",*xargv,"'");
+	}
+    }
+    if (!action)			/* Nothing to do, give usage message */
+      usage();
+
+#ifdef DEBUG
+    debug(DB_LOG,"SIMULATED ERROR RATE:",0,errorrate);
+    if (errorrate) srand(seed);		/* Init random error generator */
+#endif /* DEBUG */
+
+/* THE REAL STUFF IS FROM HERE DOWN */
+
+    if (!devopen("dummy"))		/* Open the communication device */
+      doexit(FAILURE);
+    if (!devsettings("dummy"))		/* Perform any needed settings */
+      doexit(FAILURE);
+    if (db)				/* Open debug log if requested */
+      debug(DB_OPN,"debug.log",0,0);
+
+    debug(DB_MSG,"Initializing...",0,0);
+
+/*  Fill in parameters for this run */
+
+    k.xfermode = xmode;			/* Text/binary automatic/manual  */
+    k.remote = remote;			/* Remote vs local */
+    k.binary = ftype;			/* 0 = text, 1 = binary */
+    k.parity = parity;                  /* Communications parity */
+    k.bct = (check == 5) ? 3 : check;	/* Block check type */
+    k.ikeep = keep;			/* Keep incompletely received files */
+    k.filelist = cmlist;		/* List of files to send (if any) */
+    k.cancel = 0;			/* Not canceled yet */
+
+/*  Fill in the i/o pointers  */
+  
+    k.zinbuf = i_buf;			/* File input buffer */
+    k.zinlen = IBUFLEN;			/* File input buffer length */
+    k.zincnt = 0;			/* File input buffer position */
+    k.obuf = o_buf;			/* File output buffer */
+    k.obuflen = OBUFLEN;		/* File output buffer length */
+    k.obufpos = 0;			/* File output buffer position */
+
+/* Fill in function pointers */
+
+    k.rxd    = readpkt;			/* for reading packets */
+    k.txd    = tx_data;			/* for sending packets */
+    k.ixd    = inchk;			/* for checking connection */
+    k.openf  = openfile;                /* for opening files */
+    k.finfo  = fileinfo;                /* for getting file info */
+    k.readf  = readfile;		/* for reading files */
+    k.writef = writefile;               /* for writing to output file */
+    k.closef = closefile;               /* for closing files */
+#ifdef DEBUG
+    k.dbf    = db ? dodebug : 0;	/* for debugging */
+#else
+    k.dbf    = 0;
+#endif /* DEBUG */
+    /* Force Type 3 Block Check (16-bit CRC) on all packets, or not */
+    k.bctf   = (check == 5) ? 1 : 0;
+
+/* Initialize Kermit protocol */
+
+    status = kermit(K_INIT, &k, 0, 0, "", &r);
+#ifdef DEBUG
+    debug(DB_LOG,"init status:",0,status);
+    debug(DB_LOG,"version:",k.version,0);
+#endif /* DEBUG */
+    if (status == X_ERROR)
+      doexit(FAILURE);
+    if (action == A_SEND)
+      status = kermit(K_SEND, &k, 0, 0, "", &r);
+/*
+  Now we read a packet ourselves and call Kermit with it.  Normally, Kermit
+  would read its own packets, but in the embedded context, the device must be
+  free to do other things while waiting for a packet to arrive.  So the real
+  control program might dispatch to other types of tasks, of which Kermit is
+  only one.  But in order to read a packet into Kermit's internal buffer, we
+  have to ask for a buffer address and slot number.
+
+  To interrupt a transfer in progress, set k.cancel to I_FILE to interrupt
+  only the current file, or to I_GROUP to cancel the current file and all
+  remaining files.  To cancel the whole operation in such a way that the
+  both Kermits return an error status, call Kermit with K_ERROR.
+*/
+    while (status != X_DONE) {
+/*
+  Here we block waiting for a packet to come in (unless readpkt times out).
+  Another possibility would be to call inchk() to see if any bytes are waiting
+  to be read, and if not, go do something else for a while, then come back
+  here and check again.
+*/
+        inbuf = getrslot(&k,&r_slot);	/* Allocate a window slot */
+        rx_len = k.rxd(&k,inbuf,P_PKTLEN); /* Try to read a packet */
+        debug(DB_PKT,"main packet",&(k.ipktbuf[0][r_slot]),rx_len);
+/*
+  For simplicity, kermit() ACKs the packet immediately after verifying it was
+  received correctly.  If, afterwards, the control program fails to handle the
+  data correctly (e.g. can't open file, can't write data, can't close file),
+  then it tells Kermit to send an Error packet next time through the loop.
+*/
+        if (rx_len < 1) {               /* No data was read */
+            freerslot(&k,r_slot);	/* So free the window slot */
+            if (rx_len < 0)             /* If there was a fatal error */
+              doexit(FAILURE);          /* give up */
+
+	    /* This would be another place to dispatch to another task */
+	    /* while waiting for a Kermit packet to show up. */
+
+        }
+        /* Handle the input */
+
+        switch (status = kermit(K_RUN, &k, r_slot, rx_len, "", &r)) {
+	  case X_OK:
+#ifdef DEBUG
+/*
+  This shows how, after each packet, you get the protocol state, file name,
+  date, size, and bytes transferred so far.  These can be used in a
+  file-transfer progress display, log, etc.
+*/
+	    debug(DB_LOG,"NAME",r.filename ? (char *)r.filename : "(NULL)",0);
+	    debug(DB_LOG,"DATE",r.filedate ? (char *)r.filedate : "(NULL)",0);
+	    debug(DB_LOG,"SIZE",0,r.filesize);
+	    debug(DB_LOG,"STATE",0,r.status);
+	    debug(DB_LOG,"SOFAR",0,r.sofar);
+#endif /* DEBUG */
+	    /* Maybe do other brief tasks here... */
+	    continue;			/* Keep looping */
+	  case X_DONE:
+	    break;			/* Finished */
+	  case X_ERROR:
+	    doexit(FAILURE);		/* Failed */
+	}
+    }
+    doexit(SUCCESS);
+}
diff --git a/main.c.diff b/main.c.diff
new file mode 100644
index 0000000..11a3808
--- /dev/null
+++ b/main.c.diff
@@ -0,0 +1,64 @@
+*** ../../ek16/main.c	2011-03-30 12:40:53.830806000 -0400
+--- main.c	2011-06-06 15:33:45.997789000 -0400
+***************
+*** 124,130 ****
+  #endif /* RECVONLY */
+      fprintf(stderr," -p [neoms]   Parity: none, even, odd, mark, space\n");
+  #ifdef F_CRC
+!     fprintf(stderr," -b [123]     Block check type: 1, 2, or 3\n");
+  #endif /* F_CRC */
+      fprintf(stderr," -k           Keep incompletely received files\n");
+      fprintf(stderr," -B           Force binary mode\n");
+--- 124,130 ----
+  #endif /* RECVONLY */
+      fprintf(stderr," -p [neoms]   Parity: none, even, odd, mark, space\n");
+  #ifdef F_CRC
+!     fprintf(stderr," -b [1235]    Block check type: 1, 2, 3, or 5\n");
+  #endif /* F_CRC */
+      fprintf(stderr," -k           Keep incompletely received files\n");
+      fprintf(stderr," -B           Force binary mode\n");
+***************
+*** 219,225 ****
+  	    }
+  	    if (c == 'b') {
+  		check = atoi(*xargv);
+! 		if (check < 1 || check > 3)
+  		  fatal("Invalid block check",(char *)0,(char *)0);
+  #ifdef DEBUG
+  	    } else if (c == 'E') {
+--- 219,225 ----
+  	    }
+  	    if (c == 'b') {
+  		check = atoi(*xargv);
+! 		if (check < 1 || check > 5 || check == 4)
+  		  fatal("Invalid block check",(char *)0,(char *)0);
+  #ifdef DEBUG
+  	    } else if (c == 'E') {
+***************
+*** 338,344 ****
+      k.remote = remote;			/* Remote vs local */
+      k.binary = ftype;			/* 0 = text, 1 = binary */
+      k.parity = parity;                  /* Communications parity */
+!     k.bct = check;			/* Block check type */
+      k.ikeep = keep;			/* Keep incompletely received files */
+      k.filelist = cmlist;		/* List of files to send (if any) */
+      k.cancel = 0;			/* Not canceled yet */
+--- 338,344 ----
+      k.remote = remote;			/* Remote vs local */
+      k.binary = ftype;			/* 0 = text, 1 = binary */
+      k.parity = parity;                  /* Communications parity */
+!     k.bct = (check == 5) ? 3 : check;	/* Block check type */
+      k.ikeep = keep;			/* Keep incompletely received files */
+      k.filelist = cmlist;		/* List of files to send (if any) */
+      k.cancel = 0;			/* Not canceled yet */
+***************
+*** 367,372 ****
+--- 367,374 ----
+  #else
+      k.dbf    = 0;
+  #endif /* DEBUG */
++     /* Force Type 3 Block Check (16-bit CRC) on all packets, or not */
++     k.bctf   = (check == 5) ? 1 : 0;
+  
+  /* Initialize Kermit protocol */
+  
diff --git a/makefile b/makefile
new file mode 100644
index 0000000..a098f6d
--- /dev/null
+++ b/makefile
@@ -0,0 +1,69 @@
+#Makefile for embedded Kermit.
+#
+# Copyright (C) 1995, 2011,
+#  Trustees of Columbia University in the City of New York.
+#  All Rights Reserved.  See kermit.c for license.
+
+OBJS= main.o kermit.o unixio.o
+EK = makewhat
+ALL = $(EK)
+
+all: $(ALL)
+
+ek: $(OBJS)
+	$(CC) $(CFLAGS) -o ek $(OBJS)
+
+#Dependencies
+
+main.o: main.c cdefs.h debug.h kermit.h platform.h
+
+kermit.o: kermit.c cdefs.h debug.h kermit.h
+
+unixio.o: unixio.c cdefs.h debug.h platform.h kermit.h
+
+#Targets
+
+#Build with cc.
+cc:
+	make ek
+
+#Build with gcc.
+gcc:
+	@UNAME=`uname` ; make "CC=gcc" "CC2=gcc" "CFLAGS=-D$$UNAME -O2" ek
+
+#Ditto but no debugging.
+gccnd:
+	make "CC=gcc" "CC2=gcc" "CFLAGS=-DNODEBUG -O2" ek
+
+#Build with gcc, Receive-Only, minimum size and features.
+gccmin:
+	make "CC=gcc" "CC2=gcc" \
+	"CFLAGS=-DMINSIZE -DOBUFLEN=256 -DFN_MAX=16 -O2" ek
+
+#Ditto but Receive-Only:
+gccminro:
+	make "CC=gcc" "CC2=gcc" \
+	"CFLAGS=-DMINSIZE -DOBUFLEN=256 -DFN_MAX=16 -DRECVONLY -O2" ek
+
+#Minimum size, receive-only, but with debugging:
+gccminrod:
+	make "CC=gcc" "CC2=gcc" \
+	"CFLAGS=-DMINSIZE -DOBUFLEN=256 -DFN_MAX=16 -DRECVONLY -DDEBUG -O2" ek
+
+#HP-UX 9.0 or higher with ANSI C.
+hp:
+	make "SHELL=/usr/bin/sh" CC=/opt/ansic/bin/cc CC2=/opt/ansic/bin/cc \
+	ek "CFLAGS=-DHPUX -Aa"
+
+#To get profile, build this target, run it, then "gprof ./ek > file".
+gprof:
+	make "CC=gcc" "CC2=gcc" ek "CFLAGS=-DNODEBUG -pg" "LNKFLAGS=-pg"
+
+clean:
+	rm -f $(OBJS) core
+
+makewhat:
+	@echo 'Defaulting to gcc...'
+	make gcc
+
+#End of Makefile
diff --git a/platform.h b/platform.h
new file mode 100644
index 0000000..42259d1
--- /dev/null
+++ b/platform.h
@@ -0,0 +1,12 @@
+/* Unix platform.h for EK */
+
+#include <stdio.h>
+#include <sys/stat.h>
+
+#ifndef IBUFLEN
+#define IBUFLEN  4096			/* File input buffer size */
+#endif /* IBUFLEN */
+
+#ifndef OBUFLEN
+#define OBUFLEN  8192                   /* File output buffer size */
+#endif /* OBUFLEN */
diff --git a/unix.h b/unix.h
new file mode 100644
index 0000000..42259d1
--- /dev/null
+++ b/unix.h
@@ -0,0 +1,12 @@
+/* Unix platform.h for EK */
+
+#include <stdio.h>
+#include <sys/stat.h>
+
+#ifndef IBUFLEN
+#define IBUFLEN  4096			/* File input buffer size */
+#endif /* IBUFLEN */
+
+#ifndef OBUFLEN
+#define OBUFLEN  8192                   /* File output buffer size */
+#endif /* OBUFLEN */
diff --git a/unixio.c b/unixio.c
new file mode 100644
index 0000000..6a7e0f0
--- /dev/null
+++ b/unixio.c
@@ -0,0 +1,627 @@
+/* Sample system-dependent communications i/o routines for embedded Kermit. */
+
+/*
+  Author: Frank da Cruz.
+  Copyright (C) 1995, 2011.
+  Trustees of Columbia University in the City of New York.
+  All rights reserved.
+  See kermit.c for license.
+*/
+
+/*
+  The sample i/o routines for UNIX that provide packet i/o
+  functions on the console (login) device.
+  Copy this file, rename it appropriately, and replace the contents
+  of each routine appropriately for your platform.
+
+  Device i/o:
+
+    int devopen()    Communications device - open
+    int pktmode()    Communications device - enter/exit packet mode
+    int readpkt()    Communications device - read a packet
+    int tx_data()    Communications device - send data
+    int devclose()   Communications device - close
+    int inchk()      Communications device - check if bytes are ready to read
+
+  File i/o:
+
+    int openfile()   File - open for input or output
+    ULONG fileinfo() Get input file modtime and size
+    int readfile()   Input file - read data
+    int writefile()  Output file - write data
+    int closefile()  Input or output file - close
+
+  Full definitions below, prototypes in kermit.h.
+
+  These routines must handle speed setting, parity, flow control, file i/o,
+  and similar items without the kermit() routine knowing anything about it.
+  If parity is in effect, these routines must add it to outbound characters
+  and strip it from inbound characters.
+*/
+#include <stdio.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <errno.h>
+#ifndef O_WRONLY
+#include <sys/file.h>
+#ifdef X_OK
+#undef X_OK
+#endif /* X_OK */
+#endif /* O_WRONLY */
+
+#include "cdefs.h"
+#include "debug.h"
+#include "platform.h"
+#include "kermit.h"
+
+UCHAR o_buf[OBUFLEN+8];			/* File output buffer */
+UCHAR i_buf[IBUFLEN+8];			/* File output buffer */
+
+/*
+  In this example, the output file is unbuffered to ensure that every
+  output byte is commited.  The input file, however, is buffered for speed.
+  This is just one of many possible implmentation choices, invisible to the
+  Kermit protocol module.
+*/
+static int ttyfd, ofile = -1;		/* File descriptors */
+static FILE * ifile = (FILE *)0;	/* and pointers */
+
+/* Debugging */
+
+#ifdef DEBUG
+static FILE * dp = (FILE *)0;		/* Debug log */
+static int xdebug = 0;			/* Debugging on/off */
+
+void
+dodebug(int fc, UCHAR * label, UCHAR * sval, long nval) {
+
+    if (fc != DB_OPN && !xdebug)
+      return;
+    if (!label)
+      label = "";
+
+    switch (fc) {			/* Function code */
+      case DB_OPN:			/* Open debug log */
+	if (dp) fclose(dp);
+	if (!*label) label = "debug.log";
+	dp = fopen(label,"w");
+	if (!dp) {
+	    dp = stderr;
+	} else {
+	    setbuf(dp,(char *)0);
+	}
+	xdebug = 1;
+	fprintf(dp,"DEBUG LOG OPEN\n");
+	return;
+      case DB_MSG:			/* Write a message */
+	if (dp) fprintf(dp,"%s\n",label);
+	return;
+      case DB_CHR:			/* Write label and character */
+	if (dp) fprintf(dp,"%s=[%c]\n",label,(char)nval);
+	return;
+      case DB_PKT:			/* Log a packet */
+	/* (fill in later, fall thru for now...) */
+      case DB_LOG:			/* Write label and string or number */
+	if (sval && dp)
+	  fprintf(dp,"%s[%s]\n",label,sval);
+	else
+	  fprintf(dp,"%s=%ld\n",label,nval);
+	return;
+      case DB_CLS:			/* Close debug log */
+	if (dp) {
+	    fclose(dp);
+	    dp = (FILE *)0;
+	}
+	xdebug = 0;
+    }
+}
+#endif /* DEBUG */
+
+/*  D E V O P E N  --  Open communications device  */
+/*  
+
+  Call with: string pointer to device name.  This routine should get the
+  current device settings and save them so devclose() can restore them.
+  It should open the device.  If the device is a serial port, devopen()
+  set the speed, stop bits, flow control, etc.
+  Returns: 0 on failure, 1 on success.
+*/
+int
+devopen(char *device) {
+    ttyfd = 0;
+    return(1);
+}
+
+/*  P K T M O D E  --  Put communications device into or out of packet mode  */
+/*  
+  Call with: 0 to put in normal (cooked) mode, 1 to put in packet (raw) mode.
+  For a "dumb i/o device" like an i/o port that does not have a login attached
+  to it, this routine can usually be a no-op.
+  Returns: 0 on failure, 1 on success.
+*/
+int
+pktmode(short on) {
+    if (ttyfd < 0)                      /* Device must be open */
+      return(0);
+    system(on ? "stty raw -echo" : "stty sane"); /* Crude but effective */
+    return(1);
+}
+
+
+/*  D E V S E T T I N G S  */
+
+int
+devsettings(char * s) {
+    /* Get current device settings, save them for devrestore() */
+    /* Parse string s, do whatever it says, e.g. "9600;8N1" */
+    if (!pktmode(ON))			/* And put device in packet mode */
+      return(0);
+    return(1);
+}
+
+/*  D E V R E S T O R E  */
+
+int
+devrestore(void) {
+    /* Put device back as we found it */
+    pktmode(OFF);
+    return(1);
+}
+
+
+/*  D E V C L O S E  --  Closes the current open communications device  */
+/*  
+  Call with: nothing
+  Closes the device and puts it back the way it was found by devopen().
+  Returns: 0 on failure, 1 on success.
+*/
+int
+devclose(void) {
+    ttyfd = -1;
+    return(1);
+}
+
+/* I N C H K  --  Check if input waiting */
+
+/*
+  Check if input is waiting to be read, needed for sliding windows.  This
+  sample version simply looks in the stdin buffer (which is not portable
+  even among different Unixes).  If your platform does not provide a way to
+  look at the device input buffer without blocking and without actually
+  reading from it, make this routine return -1.  On success, returns the
+  numbers of characters waiting to be read, i.e. that can be safely read
+  without blocking.
+*/
+int
+inchk(struct k_data * k) {
+#ifdef _IO_file_flags			/* Linux */
+    if (ttyfd < 0)                      /* Device must be open */
+      return(0);
+    return((int) ((stdin->_IO_read_end) - (stdin->_IO_read_ptr)));
+#else
+#ifdef AIX				/* AIX */
+    if (ttyfd < 0)
+      return(0);
+    return(stdin->_cnt);
+#else
+#ifdef SunOS				/* Solaris and SunOS */
+    if (ttyfd < 0)
+      return(0);
+    return(stdin->_cnt);
+#else  
+#ifdef HPUX				/* HPUX */
+    if (ttyfd < 0)
+      return(0);
+    return(stdin->__cnt);
+#else
+    return(-1);
+#endif /* HPUX */
+#endif /* SunOS */
+#endif /* AIX */
+#endif /* _IO_file_flags */
+}
+
+/*  R E A D P K T  --  Read a Kermit packet from the communications device  */
+/*
+  Call with:
+    k   - Kermit struct pointer
+    p   - pointer to read buffer
+    len - length of read buffer
+
+  When reading a packet, this function looks for start of Kermit packet
+  (k->r_soh), then reads everything between it and the end of the packet
+  (k->r_eom) into the indicated buffer.  Returns the number of bytes read, or:
+     0   - timeout or other possibly correctable error;
+    -1   - fatal error, such as loss of connection, or no buffer to read into.
+*/
+
+int
+readpkt(struct k_data * k, UCHAR *p, int len, int fc) {
+    int x, n, max;
+    short flag;
+    UCHAR c;
+/*
+  Timeout not implemented in this sample.
+  It should not be needed.  All non-embedded Kermits that are capable of
+  making connections are also capable of timing out, and only one Kermit
+  needs to time out.  NOTE: This simple example waits for SOH and then
+  reads everything up to the negotiated packet terminator.  A more robust
+  version might be driven by the value of the packet-length field.
+*/
+#ifdef DEBUG
+    char * p2;
+#endif	/* DEBUG */
+
+#ifdef F_CTRLC
+    short ccn;
+    ccn = 0;
+#endif /* F_CTRLC */
+
+    if (ttyfd < 0 || !p) {		/* Device not open or no buffer */
+	debug(DB_MSG,"readpkt FAIL",0,0);
+	return(-1);
+    }
+    flag = n = 0;                       /* Init local variables */
+
+#ifdef DEBUG
+    p2 = p;
+#endif	/* DEBUG */
+
+    while (1) {
+        x = getchar();                  /* Replace this with real i/o */
+        c = (k->parity) ? x & 0x7f : x & 0xff; /* Strip parity */
+
+#ifdef F_CTRLC
+	/* In remote mode only: three consecutive ^C's to quit */
+        if (k->remote && c == (UCHAR) 3) {
+            if (++ccn > 2) {
+		debug(DB_MSG,"readpkt ^C^C^C",0,0);
+		return(-1);
+	    }
+        } else {
+	    ccn = 0;
+	}
+#endif /* F_CTRLC */
+
+        if (!flag && c != k->r_soh)	/* No start of packet yet */
+          continue;                     /* so discard these bytes. */
+        if (c == k->r_soh) {		/* Start of packet */
+            flag = 1;                   /* Remember */
+            continue;                   /* But discard. */
+        } else if (c == k->r_eom	/* Packet terminator */
+		   || c == '\012'	/* 1.3: For HyperTerminal */
+		   ) {
+#ifdef DEBUG
+            *p = NUL;                   /* Terminate for printing */
+	    debug(DB_PKT,"RPKT",p2,n);
+#endif /* DEBUG */
+            return(n);
+        } else {                        /* Contents of packet */
+            if (n++ > k->r_maxlen)	/* Check length */
+              return(0);
+            else
+              *p++ = x & 0xff;
+        }
+    }
+    debug(DB_MSG,"READPKT FAIL (end)",0,0);
+    return(-1);
+}
+
+/*  T X _ D A T A  --  Writes n bytes of data to communication device.  */
+/*
+  Call with:
+    k = pointer to Kermit struct.
+    p = pointer to data to transmit.
+    n = length.
+  Returns:
+    X_OK on success.
+    X_ERROR on failure to write - i/o error.
+*/
+int
+tx_data(struct k_data * k, UCHAR *p, int n) {
+    int x;
+    int max;
+
+    max = 10;                           /* Loop breaker */
+
+    while (n > 0) {                     /* Keep trying till done */
+        x = write(ttyfd,p,n);
+        debug(DB_MSG,"tx_data write",0,x);
+        if (x < 0 || --max < 1)         /* Errors are fatal */
+          return(X_ERROR);
+        n -= x;
+	p += x;
+    }
+    return(X_OK);                       /* Success */
+}
+
+/*  O P E N F I L E  --  Open output file  */
+/*
+  Call with:
+    Pointer to filename.
+    Size in bytes.
+    Creation date in format yyyymmdd hh:mm:ss, e.g. 19950208 14:00:00
+    Mode: 1 = read, 2 = create, 3 = append.
+  Returns:
+    X_OK on success.
+    X_ERROR on failure, including rejection based on name, size, or date.    
+*/
+int
+openfile(struct k_data * k, UCHAR * s, int mode) {
+
+    switch (mode) {
+      case 1:				/* Read */
+	if (!(ifile = fopen(s,"r"))) {
+	    debug(DB_LOG,"openfile read error",s,0);
+	    return(X_ERROR);
+	}
+	k->s_first   = 1;		/* Set up for getkpt */
+	k->zinbuf[0] = '\0';		/* Initialize buffer */
+	k->zinptr    = k->zinbuf;	/* Set up buffer pointer */
+	k->zincnt    = 0;		/* and count */
+	debug(DB_LOG,"openfile read ok",s,0);
+	return(X_OK);
+
+      case 2:				/* Write (create) */
+        ofile = creat(s,0644);
+	if (ofile < 0) {
+	    debug(DB_LOG,"openfile write error",s,0);
+	    return(X_ERROR);
+	}
+	debug(DB_LOG,"openfile write ok",s,0);
+	return(X_OK);
+
+#ifdef COMMENT
+      case 3:				/* Append (not used) */
+        ofile = open(s,O_WRONLY|O_APPEND);
+	if (ofile < 0) {
+	    debug(DB_LOG,"openfile append error",s,0);
+	    return(X_ERROR);
+	}
+	    debug(DB_LOG,"openfile append ok",s,0);
+	return(X_OK);
+#endif /* COMMENT */
+
+      default:
+        return(X_ERROR);
+    }
+}
+
+/*  F I L E I N F O  --  Get info about existing file  */
+/*
+  Call with:
+    Pointer to filename
+    Pointer to buffer for date-time string
+    Length of date-time string buffer (must be at least 18 bytes)
+    Pointer to int file type:
+       0: Prevailing type is text.
+       1: Prevailing type is binary.
+    Transfer mode (0 = auto, 1 = manual):
+       0: Figure out whether file is text or binary and return type.
+       1: (nonzero) Don't try to figure out file type. 
+  Returns:
+    X_ERROR on failure.
+    0L or greater on success == file length.
+    Date-time string set to yyyymmdd hh:mm:ss modtime of file.
+    If date can't be determined, first byte of buffer is set to NUL.
+    Type set to 0 (text) or 1 (binary) if mode == 0.
+*/
+#ifdef F_SCAN
+#define SCANBUF 1024
+#define SCANSIZ 49152
+#endif /* F_SCAN */
+
+ULONG
+fileinfo(struct k_data * k,
+	 UCHAR * filename, UCHAR * buf, int buflen, short * type, short mode) {
+    struct stat statbuf;
+    struct tm * timestamp, * localtime();
+
+#ifdef F_SCAN
+    FILE * fp;				/* File scan pointer */
+    char inbuf[SCANBUF];		/* and buffer */
+#endif /* F_SCAN */
+
+    if (!buf)
+      return(X_ERROR);
+    buf[0] = '\0';
+    if (buflen < 18)
+      return(X_ERROR);
+    if (stat(filename,&statbuf) < 0)
+      return(X_ERROR);
+    timestamp = localtime(&(statbuf.st_mtime));
+    sprintf(buf,"%04d%02d%02d %02d:%02d:%02d",
+	    timestamp->tm_year + 1900,
+            timestamp->tm_mon + 1,
+            timestamp->tm_mday,
+            timestamp->tm_hour,
+            timestamp->tm_min,
+            timestamp->tm_sec
+	    );
+#ifdef F_SCAN
+/*
+  Here we determine if the file is text or binary if the transfer mode is
+  not forced.  This is an extremely crude sample, which diagnoses any file
+  that contains a control character other than HT, LF, FF, or CR as binary.
+  A more thorough content analysis can be done that accounts for various
+  character sets as well as various forms of Unicode (UTF-8, UTF-16, etc).
+  Or the diagnosis could be based wholly or in part on the filename.
+  etc etc.  Or the implementation could skip this entirely by not defining
+  F_SCAN and/or by always calling this routine with type set to -1.
+*/
+    if (!mode) {			/* File type determination requested */
+	int isbinary = 1;
+	fp = fopen(filename,"r");	/* Open the file for scanning */
+	if (fp) {
+	    int n = 0, count = 0;
+	    char c, * p;
+
+	    debug(DB_LOG,"fileinfo scan ",filename,0);
+
+	    isbinary = 0;
+	    while (count < SCANSIZ && !isbinary) { /* Scan this much */
+		n = fread(inbuf,1,SCANBUF,fp);
+		if (n == EOF || n == 0)
+		  break;
+		count += n;
+		p = inbuf;
+		while (n--) {
+		    c = *p++;
+		    if (c < 32 || c == 127) {
+			if (c !=  9 &&	/* Tab */
+			    c != 10 &&	/* LF */
+			    c != 12 &&	/* FF */
+			    c != 13) {	/* CR */
+			    isbinary = 1;
+			    debug(DB_MSG,"fileinfo BINARY",0,0);
+			    break;
+			}
+		    }
+		}
+	    }
+	    fclose(fp);
+	    *type = isbinary;
+	}
+    }
+#endif /* F_SCAN */
+
+    return((ULONG)(statbuf.st_size));
+}
+
+
+/*  R E A D F I L E  --  Read data from a file  */
+
+int
+readfile(struct k_data * k) {
+    if (!k->zinptr) {
+#ifdef DEBUG
+	fprintf(dp,"readfile ZINPTR NOT SET\n");
+#endif /* DEBUG */
+	return(X_ERROR);
+    }
+    if (k->zincnt < 1) {		/* Nothing in buffer - must refill */
+	if (k->binary) {		/* Binary - just read raw buffers */
+	    k->dummy = 0;
+	    k->zincnt = fread(k->zinbuf, 1, k->zinlen, ifile);
+	    debug(DB_LOG,"readfile binary ok zincnt",0,k->zincnt);
+
+	} else {			/* Text mode needs LF/CRLF handling */
+	    int c;			/* Current character */
+	    for (k->zincnt = 0; (k->zincnt < (k->zinlen - 2)); (k->zincnt)++) {
+		if ((c = getc(ifile)) == EOF)
+		  break;
+		if (c == '\n')		/* Have newline? */
+		  k->zinbuf[(k->zincnt)++] = '\r'; /* Insert CR */
+		k->zinbuf[k->zincnt] = c;
+	    }
+#ifdef DEBUG
+	    k->zinbuf[k->zincnt] = '\0';
+	    debug(DB_LOG,"readfile text ok zincnt",0,k->zincnt);
+#endif /* DEBUG */
+	}
+	k->zinbuf[k->zincnt] = '\0';	/* Terminate. */
+	if (k->zincnt == 0)		/* Check for EOF */
+	  return(-1);
+	k->zinptr = k->zinbuf;		/* Not EOF - reset pointer */
+    }
+    (k->zincnt)--;			/* Return first byte. */
+
+    debug(DB_LOG,"readfile exit zincnt",0,k->zincnt);
+    debug(DB_LOG,"readfile exit zinptr",0,k->zinptr);
+    return(*(k->zinptr)++ & 0xff);
+}
+
+
+/*  W R I T E F I L E  --  Write data to file  */
+/*
+  Call with:
+    Kermit struct
+    String pointer
+    Length
+  Returns:
+    X_OK on success
+    X_ERROR on failure, such as i/o error, space used up, etc
+*/
+int
+writefile(struct k_data * k, UCHAR * s, int n) {
+    int rc;
+    rc = X_OK;
+
+    debug(DB_LOG,"writefile binary",0,k->binary);
+
+    if (k->binary) {			/* Binary mode, just write it */
+	if (write(ofile,s,n) != n)
+	  rc = X_ERROR;
+    } else {				/* Text mode, skip CRs */
+	UCHAR * p, * q;
+	int i;
+	q = s;
+
+	while (1) {
+	    for (p = q, i = 0; ((*p) && (*p != (UCHAR)13)); p++, i++) ;
+	    if (i > 0)
+	      if (write(ofile,q,i) != i)
+		rc = X_ERROR;
+	    if (!*p) break;
+	    q = p+1;
+	}
+    }
+    return(rc);
+}
+
+/*  C L O S E F I L E  --  Close output file  */
+/*
+  Mode = 1 for input file, mode = 2 or 3 for output file.
+
+  For output files, the character c is the character (if any) from the Z
+  packet data field.  If it is D, it means the file transfer was canceled
+  in midstream by the sender, and the file is therefore incomplete.  This
+  routine should check for that and decide what to do.  It should be
+  harmless to call this routine for a file that that is not open.
+*/
+int
+closefile(struct k_data * k, UCHAR c, int mode) {
+    int rc = X_OK;			/* Return code */
+
+    switch (mode) {
+      case 1:				/* Closing input file */
+	if (!ifile)			/* If not not open */
+	  break;			/* do nothing but succeed */
+	debug(DB_LOG,"closefile (input)",k->filename,0);
+	if (fclose(ifile) < 0)
+	  rc = X_ERROR;
+	break;
+      case 2:				/* Closing output file */
+      case 3:
+	if (ofile < 0)			/* If not open */
+	  break;			/* do nothing but succeed */
+	debug(DB_LOG,"closefile (output) name",k->filename,0);
+	debug(DB_LOG,"closefile (output) keep",0,k->ikeep);
+	if (close(ofile) < 0) {		/* Try to close */
+	    rc = X_ERROR;
+	} else if ((k->ikeep == 0) &&	/* Don't keep incomplete files */
+		   (c == 'D')) {	/* This file was incomplete */
+	    if (k->filename) {
+		debug(DB_LOG,"deleting incomplete",k->filename,0);
+		unlink(k->filename);	/* Delete it. */
+	    }
+	}
+	break;
+      default:
+	rc = X_ERROR;
+    }
+    return(rc);
+}
+
+#ifdef DEBUG
+int xerror() {
+    unsigned int x;
+    extern int errorrate;		/* Fix this - NO EXTERNS */
+    if (!errorrate)
+      return(0);
+    x = rand() % 100;			/* Fix this - NO C LIBRARY */
+    debug(DB_LOG,"RANDOM",0,x);
+    debug(DB_LOG,"ERROR",0,(x < errorrate));
+    return(x < errorrate);
+}
+#endif /* DEBUG */



More information about the Commits mailing list