From 04d48e513f2094345eb697c0a34345daff63a87b Mon Sep 17 00:00:00 2001 From: Paul Selkirk Date: Thu, 16 Jun 2016 15:42:58 -0400 Subject: Initial commit of unmodified E-Kermit 1.7 --- COPYING | 35 ++ cdefs.h | 33 ++ debug.h | 40 ++ ek17.diff | 232 +++++++++ kermit.c | 1619 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ kermit.c.diff | 139 +++++ kermit.h | 423 +++++++++++++++ kermit.h.diff | 29 ++ main.c | 447 ++++++++++++++++ main.c.diff | 64 +++ makefile | 69 +++ platform.h | 12 + unix.h | 12 + unixio.c | 627 ++++++++++++++++++++++ 14 files changed, 3781 insertions(+) create mode 100644 COPYING create mode 100644 cdefs.h create mode 100644 debug.h create mode 100644 ek17.diff create mode 100644 kermit.c create mode 100644 kermit.c.diff create mode 100644 kermit.h create mode 100644 kermit.h.diff create mode 100644 main.c create mode 100644 main.c.diff create mode 100644 makefile create mode 100644 platform.h create mode 100644 unix.h create mode 100644 unixio.c 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 +#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 \n",xname); + fprintf(stderr,"Options:\n"); + fprintf(stderr," -r Receive files\n"); +#ifndef RECVONLY + fprintf(stderr," -s 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 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 +#include + +#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 +#include + +#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 +#include +#include +#include +#ifndef O_WRONLY +#include +#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 */ -- cgit v1.2.3