summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--COPYING35
-rw-r--r--cdefs.h33
-rw-r--r--debug.h40
-rw-r--r--ek17.diff232
-rw-r--r--kermit.c1619
-rw-r--r--kermit.c.diff139
-rw-r--r--kermit.h423
-rw-r--r--kermit.h.diff29
-rw-r--r--main.c447
-rw-r--r--main.c.diff64
-rw-r--r--makefile69
-rw-r--r--platform.h12
-rw-r--r--unix.h12
-rw-r--r--unixio.c627
14 files changed, 3781 insertions, 0 deletions
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 */