diff options
-rw-r--r-- | COPYING | 35 | ||||
-rw-r--r-- | cdefs.h | 33 | ||||
-rw-r--r-- | debug.h | 40 | ||||
-rw-r--r-- | ek17.diff | 232 | ||||
-rw-r--r-- | kermit.c | 1619 | ||||
-rw-r--r-- | kermit.c.diff | 139 | ||||
-rw-r--r-- | kermit.h | 423 | ||||
-rw-r--r-- | kermit.h.diff | 29 | ||||
-rw-r--r-- | main.c | 447 | ||||
-rw-r--r-- | main.c.diff | 64 | ||||
-rw-r--r-- | makefile | 69 | ||||
-rw-r--r-- | platform.h | 12 | ||||
-rw-r--r-- | unix.h | 12 | ||||
-rw-r--r-- | unixio.c | 627 |
14 files changed, 3781 insertions, 0 deletions
@@ -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. @@ -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__ */ @@ -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; + }; + @@ -0,0 +1,447 @@ +/* Embedded Kermit demo, main program. */ + +/* + Author: Frank da Cruz, the Kermit Project, Columbia University, New York. + Copyright (C) 1995, 2011, + Trustees of Columbia University in the City of New York. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name of Columbia University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + This is a demo/test framework, to be replaced by a real control program. + It includes a simple Unix-style command-line parser to allow setup and + testing of the Kermit module. Skip past all this to where it says REAL + STUFF to see the real stuff. ANSI C required. Note: order of the + following includes is important. +*/ +#include "cdefs.h" /* Data types for all modules */ +#include "debug.h" /* Debugging */ +#include "platform.h" /* Platform-specific includes and definitions */ +#include "kermit.h" /* Kermit symbols and data structures */ +#ifdef __linux +#include <errno.h> +#endif /* __linux */ + +/* + Sample prototypes for i/o functions. + The functions are defined in a platform-specific i/o module. + The function names are unknown to the Kermit module. + The names can be changed but not their calling conventions. + The following prototypes are keyed to unixio.c. +*/ +int devopen(char *); /* Communications device/path */ +int devsettings(char *); +int devrestore(void); +int devclose(void); +int pktmode(short); + +int readpkt(struct k_data *, UCHAR *, int); /* Communications i/o functions */ +int tx_data(struct k_data *, UCHAR *, int); +int inchk(struct k_data *); + +int openfile(struct k_data *, UCHAR *, int); /* File i/o functions */ +int writefile(struct k_data *, UCHAR *, int); +int readfile(struct k_data *); +int closefile(struct k_data *, UCHAR, int); +ULONG fileinfo(struct k_data *, UCHAR *, UCHAR *, int, short *, short); + +/* External data */ + +extern UCHAR o_buf[]; /* Must be defined in io.c */ +extern UCHAR i_buf[]; /* Must be defined in io.c */ +extern int errno; + +/* Data global to this module */ + +struct k_data k; /* Kermit data structure */ +struct k_response r; /* Kermit response structure */ + +char **xargv; /* Global pointer to arg vector */ +UCHAR **cmlist = (UCHAR **)0; /* Pointer to file list */ +char * xname = "ek"; /* Default program name */ + +int xargc; /* Global argument count */ +int nfils = 0; /* Number of files in file list */ +int action = 0; /* Send or Receive */ +int xmode = 0; /* File-transfer mode */ +int ftype = 1; /* Global file type 0=text 1=binary*/ +int keep = 0; /* Keep incompletely received files */ +int db = 0; /* Debugging */ +short fmode = -1; /* Transfer mode for this file */ +int parity = 0; /* Parity */ +#ifdef F_CRC +int check = 3; /* Block check */ +#else +int check = 1; +#endif /* F_CRC */ +int remote = 1; /* 1 = Remote, 0 = Local */ +#ifdef DEBUG +int errorrate = 0; /* Simulated error rate */ +int seed = 1234; /* Random number generator seed */ +#endif /* DEBUG */ + +void +doexit(int status) { + devrestore(); /* Restore device */ + devclose(); /* Close device */ + exit(status); /* Exit with indicated status */ +} + +void +usage() { + fprintf(stderr,"E-Kermit %s\n",VERSION); + fprintf(stderr,"Usage: %s <options>\n",xname); + fprintf(stderr,"Options:\n"); + fprintf(stderr," -r Receive files\n"); +#ifndef RECVONLY + fprintf(stderr," -s <files> Send files\n"); +#endif /* RECVONLY */ + fprintf(stderr," -p [neoms] Parity: none, even, odd, mark, space\n"); +#ifdef F_CRC + fprintf(stderr," -b [1235] Block check type: 1, 2, 3, or 5\n"); +#endif /* F_CRC */ + fprintf(stderr," -k Keep incompletely received files\n"); + fprintf(stderr," -B Force binary mode\n"); + fprintf(stderr," -T Force text mode\n"); + fprintf(stderr," -R Remote mode (vs local)\n"); + fprintf(stderr," -L Local mode (vs remote)\n"); +#ifdef DEBUG + fprintf(stderr," -E <number> Simulated error rate (0-100)\n"); + fprintf(stderr," -d Create debug.log\n"); +#endif /* DEBUG */ + fprintf(stderr," -h Help (this message)\n"); + doexit(FAILURE); +} + +void +fatal(char *msg1, char *msg2, char *msg3) { /* Not to be called except */ + if (msg1) { /* from this module */ + fprintf(stderr,"%s: %s",xname,msg1); + if (msg2) fprintf(stderr,"%s",msg2); + if (msg3) fprintf(stderr,"%s",msg3); + fprintf(stderr,"\n"); + } + doexit(FAILURE); +} + +/* Simple user interface for testing */ + +int +doarg(char c) { /* Command-line option parser */ + int x; /* Parses one option with its arg(s) */ + char *xp, *s; + struct stat statbuf; + + xp = *xargv+1; /* Pointer for bundled args */ + while (c) { +#ifdef DEBUG + if (errorrate) seed += (int)c; +#endif /* DEBUG) */ + switch (c) { + case 'r': /* Receive */ + if (action) fatal("Conflicting actions",(char *)0,(char *)0); + action = A_RECV; + break; + +#ifndef RECVONLY + case 's': /* Send */ + if (action) + fatal("Conflicting actions",(char *)0,(char *)0); + if (*(xp+1)) + fatal("Invalid argument bundling after -s",(char *)0,(char *)0); + nfils = 0; /* Initialize file counter, flag */ + cmlist = (UCHAR **)(xargv+1); /* Remember this pointer */ + while (--xargc > 0) { /* Traverse the list */ + xargv++; + s = *xargv; +#ifdef DEBUG + if (errorrate) seed += (int)*s; +#endif /* DEBUG) */ + if (**xargv == '-') + break; + errno = 0; + x = stat(s,&statbuf); + if (x < 0) + fatal("File '",s,"' not found"); + if (access(s,4) < 0) + fatal("File '",s,"' not accessible"); + nfils++; + } + xargc++, *xargv--; /* Adjust argv/argc */ + if (nfils < 1) + fatal("Missing filename for -s",(char *)0,(char *)0); + action = A_SEND; + break; +#endif /* RECVONLY */ + +#ifdef F_CRC + case 'b': /* Block-check type */ +#endif /* F_CRC */ +#ifdef DEBUG + case 'E': /* Simulated error rate */ +#endif /* DEBUG */ + if (*(xp+1)) + fatal("Invalid argument bundling",(char *)0,(char *)0); + *xargv++, xargc--; + if ((xargc < 1) || (**xargv == '-')) + fatal("Missing option argument",(char *)0,(char *)0); + s = *xargv; + while (*s) { + if (!isdigit(*s)) + fatal("Numeric argument required",(char *)0,(char *)0); + s++; + } + if (c == 'b') { + check = atoi(*xargv); + if (check < 1 || check > 5 || check == 4) + fatal("Invalid block check",(char *)0,(char *)0); +#ifdef DEBUG + } else if (c == 'E') { + errorrate = atoi(*xargv); + if (errorrate > 100) + fatal("Invalid error rate",(char *)0,(char *)0); +#endif /* DEBUG */ + } + break; + + case 'h': /* Help */ + case '?': + usage(); + + case 'B': /* Force binary file transfer */ + xmode = 1; /* So no automatic switching */ + ftype = BINARY; + break; + + case 'T': /* Force text file transfer */ + xmode = 1; /* So no automatic switching */ + ftype = TEXT; + break; + + case 'R': /* Tell Kermit it's in remote mode */ + remote = 1; + break; + + case 'L': /* Tell Kermit it's in local mode */ + remote = 0; + break; + + case 'k': /* Keep incompletely received files */ + keep = 1; + break; + + case 'p': /* Parity */ + if (*(xp+1)) + fatal("Invalid argument bundling",(char *)0,(char *)0); + *xargv++, xargc--; + if ((xargc < 1) || (**xargv == '-')) + fatal("Missing parity",(char *)0,(char *)0); + switch(x = **xargv) { + case 'e': /* Even */ + case 'o': /* Odd */ + case 'm': /* Mark */ + case 's': parity = x; break; /* Space */ + case 'n': parity = 0; break; /* None */ + default: fatal("Invalid parity '", *xargv, "'"); + } + break; + +#ifdef DEBUG + case 'd': + db++; + break; +#endif /* DEBUG */ + + default: /* Anything else */ + fatal("Unknown command-line option ", + *xargv, + " type 'ek -h' for help." + ); + } + c = *++xp; /* See if options are bundled */ + } + return(action); +} + +void +main(int argc, char ** argv) { + int status, rx_len, i, x; + char c; + UCHAR *inbuf; + short r_slot; + + parity = P_PARITY; /* Set this to desired parity */ + status = X_OK; /* Initial kermit status */ + + xargc = argc; + xargv = argv; + xname = argv[0]; + + while (--xargc > 0) { /* Loop through command-line words */ + xargv++; + if (**xargv == '-') { /* Have dash */ + c = *(*xargv+1); /* Get the option letter */ + x = doarg(c); /* Go handle the option */ + if (x < 0) doexit(FAILURE); + } else { /* No dash where expected */ + fatal("Malformed command-line option: '",*xargv,"'"); + } + } + if (!action) /* Nothing to do, give usage message */ + usage(); + +#ifdef DEBUG + debug(DB_LOG,"SIMULATED ERROR RATE:",0,errorrate); + if (errorrate) srand(seed); /* Init random error generator */ +#endif /* DEBUG */ + +/* THE REAL STUFF IS FROM HERE DOWN */ + + if (!devopen("dummy")) /* Open the communication device */ + doexit(FAILURE); + if (!devsettings("dummy")) /* Perform any needed settings */ + doexit(FAILURE); + if (db) /* Open debug log if requested */ + debug(DB_OPN,"debug.log",0,0); + + debug(DB_MSG,"Initializing...",0,0); + +/* Fill in parameters for this run */ + + k.xfermode = xmode; /* Text/binary automatic/manual */ + k.remote = remote; /* Remote vs local */ + k.binary = ftype; /* 0 = text, 1 = binary */ + k.parity = parity; /* Communications parity */ + k.bct = (check == 5) ? 3 : check; /* Block check type */ + k.ikeep = keep; /* Keep incompletely received files */ + k.filelist = cmlist; /* List of files to send (if any) */ + k.cancel = 0; /* Not canceled yet */ + +/* Fill in the i/o pointers */ + + k.zinbuf = i_buf; /* File input buffer */ + k.zinlen = IBUFLEN; /* File input buffer length */ + k.zincnt = 0; /* File input buffer position */ + k.obuf = o_buf; /* File output buffer */ + k.obuflen = OBUFLEN; /* File output buffer length */ + k.obufpos = 0; /* File output buffer position */ + +/* Fill in function pointers */ + + k.rxd = readpkt; /* for reading packets */ + k.txd = tx_data; /* for sending packets */ + k.ixd = inchk; /* for checking connection */ + k.openf = openfile; /* for opening files */ + k.finfo = fileinfo; /* for getting file info */ + k.readf = readfile; /* for reading files */ + k.writef = writefile; /* for writing to output file */ + k.closef = closefile; /* for closing files */ +#ifdef DEBUG + k.dbf = db ? dodebug : 0; /* for debugging */ +#else + k.dbf = 0; +#endif /* DEBUG */ + /* Force Type 3 Block Check (16-bit CRC) on all packets, or not */ + k.bctf = (check == 5) ? 1 : 0; + +/* Initialize Kermit protocol */ + + status = kermit(K_INIT, &k, 0, 0, "", &r); +#ifdef DEBUG + debug(DB_LOG,"init status:",0,status); + debug(DB_LOG,"version:",k.version,0); +#endif /* DEBUG */ + if (status == X_ERROR) + doexit(FAILURE); + if (action == A_SEND) + status = kermit(K_SEND, &k, 0, 0, "", &r); +/* + Now we read a packet ourselves and call Kermit with it. Normally, Kermit + would read its own packets, but in the embedded context, the device must be + free to do other things while waiting for a packet to arrive. So the real + control program might dispatch to other types of tasks, of which Kermit is + only one. But in order to read a packet into Kermit's internal buffer, we + have to ask for a buffer address and slot number. + + To interrupt a transfer in progress, set k.cancel to I_FILE to interrupt + only the current file, or to I_GROUP to cancel the current file and all + remaining files. To cancel the whole operation in such a way that the + both Kermits return an error status, call Kermit with K_ERROR. +*/ + while (status != X_DONE) { +/* + Here we block waiting for a packet to come in (unless readpkt times out). + Another possibility would be to call inchk() to see if any bytes are waiting + to be read, and if not, go do something else for a while, then come back + here and check again. +*/ + inbuf = getrslot(&k,&r_slot); /* Allocate a window slot */ + rx_len = k.rxd(&k,inbuf,P_PKTLEN); /* Try to read a packet */ + debug(DB_PKT,"main packet",&(k.ipktbuf[0][r_slot]),rx_len); +/* + For simplicity, kermit() ACKs the packet immediately after verifying it was + received correctly. If, afterwards, the control program fails to handle the + data correctly (e.g. can't open file, can't write data, can't close file), + then it tells Kermit to send an Error packet next time through the loop. +*/ + if (rx_len < 1) { /* No data was read */ + freerslot(&k,r_slot); /* So free the window slot */ + if (rx_len < 0) /* If there was a fatal error */ + doexit(FAILURE); /* give up */ + + /* This would be another place to dispatch to another task */ + /* while waiting for a Kermit packet to show up. */ + + } + /* Handle the input */ + + switch (status = kermit(K_RUN, &k, r_slot, rx_len, "", &r)) { + case X_OK: +#ifdef DEBUG +/* + This shows how, after each packet, you get the protocol state, file name, + date, size, and bytes transferred so far. These can be used in a + file-transfer progress display, log, etc. +*/ + debug(DB_LOG,"NAME",r.filename ? (char *)r.filename : "(NULL)",0); + debug(DB_LOG,"DATE",r.filedate ? (char *)r.filedate : "(NULL)",0); + debug(DB_LOG,"SIZE",0,r.filesize); + debug(DB_LOG,"STATE",0,r.status); + debug(DB_LOG,"SOFAR",0,r.sofar); +#endif /* DEBUG */ + /* Maybe do other brief tasks here... */ + continue; /* Keep looping */ + case X_DONE: + break; /* Finished */ + case X_ERROR: + doexit(FAILURE); /* Failed */ + } + } + doexit(SUCCESS); +} diff --git a/main.c.diff b/main.c.diff new file mode 100644 index 0000000..11a3808 --- /dev/null +++ b/main.c.diff @@ -0,0 +1,64 @@ +*** ../../ek16/main.c 2011-03-30 12:40:53.830806000 -0400 +--- main.c 2011-06-06 15:33:45.997789000 -0400 +*************** +*** 124,130 **** + #endif /* RECVONLY */ + fprintf(stderr," -p [neoms] Parity: none, even, odd, mark, space\n"); + #ifdef F_CRC +! fprintf(stderr," -b [123] Block check type: 1, 2, or 3\n"); + #endif /* F_CRC */ + fprintf(stderr," -k Keep incompletely received files\n"); + fprintf(stderr," -B Force binary mode\n"); +--- 124,130 ---- + #endif /* RECVONLY */ + fprintf(stderr," -p [neoms] Parity: none, even, odd, mark, space\n"); + #ifdef F_CRC +! fprintf(stderr," -b [1235] Block check type: 1, 2, 3, or 5\n"); + #endif /* F_CRC */ + fprintf(stderr," -k Keep incompletely received files\n"); + fprintf(stderr," -B Force binary mode\n"); +*************** +*** 219,225 **** + } + if (c == 'b') { + check = atoi(*xargv); +! if (check < 1 || check > 3) + fatal("Invalid block check",(char *)0,(char *)0); + #ifdef DEBUG + } else if (c == 'E') { +--- 219,225 ---- + } + if (c == 'b') { + check = atoi(*xargv); +! if (check < 1 || check > 5 || check == 4) + fatal("Invalid block check",(char *)0,(char *)0); + #ifdef DEBUG + } else if (c == 'E') { +*************** +*** 338,344 **** + k.remote = remote; /* Remote vs local */ + k.binary = ftype; /* 0 = text, 1 = binary */ + k.parity = parity; /* Communications parity */ +! k.bct = check; /* Block check type */ + k.ikeep = keep; /* Keep incompletely received files */ + k.filelist = cmlist; /* List of files to send (if any) */ + k.cancel = 0; /* Not canceled yet */ +--- 338,344 ---- + k.remote = remote; /* Remote vs local */ + k.binary = ftype; /* 0 = text, 1 = binary */ + k.parity = parity; /* Communications parity */ +! k.bct = (check == 5) ? 3 : check; /* Block check type */ + k.ikeep = keep; /* Keep incompletely received files */ + k.filelist = cmlist; /* List of files to send (if any) */ + k.cancel = 0; /* Not canceled yet */ +*************** +*** 367,372 **** +--- 367,374 ---- + #else + k.dbf = 0; + #endif /* DEBUG */ ++ /* Force Type 3 Block Check (16-bit CRC) on all packets, or not */ ++ k.bctf = (check == 5) ? 1 : 0; + + /* Initialize Kermit protocol */ + diff --git a/makefile b/makefile new file mode 100644 index 0000000..a098f6d --- /dev/null +++ b/makefile @@ -0,0 +1,69 @@ +#Makefile for embedded Kermit. +# +# Copyright (C) 1995, 2011, +# Trustees of Columbia University in the City of New York. +# All Rights Reserved. See kermit.c for license. + +OBJS= main.o kermit.o unixio.o +EK = makewhat +ALL = $(EK) + +all: $(ALL) + +ek: $(OBJS) + $(CC) $(CFLAGS) -o ek $(OBJS) + +#Dependencies + +main.o: main.c cdefs.h debug.h kermit.h platform.h + +kermit.o: kermit.c cdefs.h debug.h kermit.h + +unixio.o: unixio.c cdefs.h debug.h platform.h kermit.h + +#Targets + +#Build with cc. +cc: + make ek + +#Build with gcc. +gcc: + @UNAME=`uname` ; make "CC=gcc" "CC2=gcc" "CFLAGS=-D$$UNAME -O2" ek + +#Ditto but no debugging. +gccnd: + make "CC=gcc" "CC2=gcc" "CFLAGS=-DNODEBUG -O2" ek + +#Build with gcc, Receive-Only, minimum size and features. +gccmin: + make "CC=gcc" "CC2=gcc" \ + "CFLAGS=-DMINSIZE -DOBUFLEN=256 -DFN_MAX=16 -O2" ek + +#Ditto but Receive-Only: +gccminro: + make "CC=gcc" "CC2=gcc" \ + "CFLAGS=-DMINSIZE -DOBUFLEN=256 -DFN_MAX=16 -DRECVONLY -O2" ek + +#Minimum size, receive-only, but with debugging: +gccminrod: + make "CC=gcc" "CC2=gcc" \ + "CFLAGS=-DMINSIZE -DOBUFLEN=256 -DFN_MAX=16 -DRECVONLY -DDEBUG -O2" ek + +#HP-UX 9.0 or higher with ANSI C. +hp: + make "SHELL=/usr/bin/sh" CC=/opt/ansic/bin/cc CC2=/opt/ansic/bin/cc \ + ek "CFLAGS=-DHPUX -Aa" + +#To get profile, build this target, run it, then "gprof ./ek > file". +gprof: + make "CC=gcc" "CC2=gcc" ek "CFLAGS=-DNODEBUG -pg" "LNKFLAGS=-pg" + +clean: + rm -f $(OBJS) core + +makewhat: + @echo 'Defaulting to gcc...' + make gcc + +#End of Makefile diff --git a/platform.h b/platform.h new file mode 100644 index 0000000..42259d1 --- /dev/null +++ b/platform.h @@ -0,0 +1,12 @@ +/* Unix platform.h for EK */ + +#include <stdio.h> +#include <sys/stat.h> + +#ifndef IBUFLEN +#define IBUFLEN 4096 /* File input buffer size */ +#endif /* IBUFLEN */ + +#ifndef OBUFLEN +#define OBUFLEN 8192 /* File output buffer size */ +#endif /* OBUFLEN */ @@ -0,0 +1,12 @@ +/* Unix platform.h for EK */ + +#include <stdio.h> +#include <sys/stat.h> + +#ifndef IBUFLEN +#define IBUFLEN 4096 /* File input buffer size */ +#endif /* IBUFLEN */ + +#ifndef OBUFLEN +#define OBUFLEN 8192 /* File output buffer size */ +#endif /* OBUFLEN */ diff --git a/unixio.c b/unixio.c new file mode 100644 index 0000000..6a7e0f0 --- /dev/null +++ b/unixio.c @@ -0,0 +1,627 @@ +/* Sample system-dependent communications i/o routines for embedded Kermit. */ + +/* + Author: Frank da Cruz. + Copyright (C) 1995, 2011. + Trustees of Columbia University in the City of New York. + All rights reserved. + See kermit.c for license. +*/ + +/* + The sample i/o routines for UNIX that provide packet i/o + functions on the console (login) device. + Copy this file, rename it appropriately, and replace the contents + of each routine appropriately for your platform. + + Device i/o: + + int devopen() Communications device - open + int pktmode() Communications device - enter/exit packet mode + int readpkt() Communications device - read a packet + int tx_data() Communications device - send data + int devclose() Communications device - close + int inchk() Communications device - check if bytes are ready to read + + File i/o: + + int openfile() File - open for input or output + ULONG fileinfo() Get input file modtime and size + int readfile() Input file - read data + int writefile() Output file - write data + int closefile() Input or output file - close + + Full definitions below, prototypes in kermit.h. + + These routines must handle speed setting, parity, flow control, file i/o, + and similar items without the kermit() routine knowing anything about it. + If parity is in effect, these routines must add it to outbound characters + and strip it from inbound characters. +*/ +#include <stdio.h> +#include <sys/stat.h> +#include <time.h> +#include <errno.h> +#ifndef O_WRONLY +#include <sys/file.h> +#ifdef X_OK +#undef X_OK +#endif /* X_OK */ +#endif /* O_WRONLY */ + +#include "cdefs.h" +#include "debug.h" +#include "platform.h" +#include "kermit.h" + +UCHAR o_buf[OBUFLEN+8]; /* File output buffer */ +UCHAR i_buf[IBUFLEN+8]; /* File output buffer */ + +/* + In this example, the output file is unbuffered to ensure that every + output byte is commited. The input file, however, is buffered for speed. + This is just one of many possible implmentation choices, invisible to the + Kermit protocol module. +*/ +static int ttyfd, ofile = -1; /* File descriptors */ +static FILE * ifile = (FILE *)0; /* and pointers */ + +/* Debugging */ + +#ifdef DEBUG +static FILE * dp = (FILE *)0; /* Debug log */ +static int xdebug = 0; /* Debugging on/off */ + +void +dodebug(int fc, UCHAR * label, UCHAR * sval, long nval) { + + if (fc != DB_OPN && !xdebug) + return; + if (!label) + label = ""; + + switch (fc) { /* Function code */ + case DB_OPN: /* Open debug log */ + if (dp) fclose(dp); + if (!*label) label = "debug.log"; + dp = fopen(label,"w"); + if (!dp) { + dp = stderr; + } else { + setbuf(dp,(char *)0); + } + xdebug = 1; + fprintf(dp,"DEBUG LOG OPEN\n"); + return; + case DB_MSG: /* Write a message */ + if (dp) fprintf(dp,"%s\n",label); + return; + case DB_CHR: /* Write label and character */ + if (dp) fprintf(dp,"%s=[%c]\n",label,(char)nval); + return; + case DB_PKT: /* Log a packet */ + /* (fill in later, fall thru for now...) */ + case DB_LOG: /* Write label and string or number */ + if (sval && dp) + fprintf(dp,"%s[%s]\n",label,sval); + else + fprintf(dp,"%s=%ld\n",label,nval); + return; + case DB_CLS: /* Close debug log */ + if (dp) { + fclose(dp); + dp = (FILE *)0; + } + xdebug = 0; + } +} +#endif /* DEBUG */ + +/* D E V O P E N -- Open communications device */ +/* + + Call with: string pointer to device name. This routine should get the + current device settings and save them so devclose() can restore them. + It should open the device. If the device is a serial port, devopen() + set the speed, stop bits, flow control, etc. + Returns: 0 on failure, 1 on success. +*/ +int +devopen(char *device) { + ttyfd = 0; + return(1); +} + +/* P K T M O D E -- Put communications device into or out of packet mode */ +/* + Call with: 0 to put in normal (cooked) mode, 1 to put in packet (raw) mode. + For a "dumb i/o device" like an i/o port that does not have a login attached + to it, this routine can usually be a no-op. + Returns: 0 on failure, 1 on success. +*/ +int +pktmode(short on) { + if (ttyfd < 0) /* Device must be open */ + return(0); + system(on ? "stty raw -echo" : "stty sane"); /* Crude but effective */ + return(1); +} + + +/* D E V S E T T I N G S */ + +int +devsettings(char * s) { + /* Get current device settings, save them for devrestore() */ + /* Parse string s, do whatever it says, e.g. "9600;8N1" */ + if (!pktmode(ON)) /* And put device in packet mode */ + return(0); + return(1); +} + +/* D E V R E S T O R E */ + +int +devrestore(void) { + /* Put device back as we found it */ + pktmode(OFF); + return(1); +} + + +/* D E V C L O S E -- Closes the current open communications device */ +/* + Call with: nothing + Closes the device and puts it back the way it was found by devopen(). + Returns: 0 on failure, 1 on success. +*/ +int +devclose(void) { + ttyfd = -1; + return(1); +} + +/* I N C H K -- Check if input waiting */ + +/* + Check if input is waiting to be read, needed for sliding windows. This + sample version simply looks in the stdin buffer (which is not portable + even among different Unixes). If your platform does not provide a way to + look at the device input buffer without blocking and without actually + reading from it, make this routine return -1. On success, returns the + numbers of characters waiting to be read, i.e. that can be safely read + without blocking. +*/ +int +inchk(struct k_data * k) { +#ifdef _IO_file_flags /* Linux */ + if (ttyfd < 0) /* Device must be open */ + return(0); + return((int) ((stdin->_IO_read_end) - (stdin->_IO_read_ptr))); +#else +#ifdef AIX /* AIX */ + if (ttyfd < 0) + return(0); + return(stdin->_cnt); +#else +#ifdef SunOS /* Solaris and SunOS */ + if (ttyfd < 0) + return(0); + return(stdin->_cnt); +#else +#ifdef HPUX /* HPUX */ + if (ttyfd < 0) + return(0); + return(stdin->__cnt); +#else + return(-1); +#endif /* HPUX */ +#endif /* SunOS */ +#endif /* AIX */ +#endif /* _IO_file_flags */ +} + +/* R E A D P K T -- Read a Kermit packet from the communications device */ +/* + Call with: + k - Kermit struct pointer + p - pointer to read buffer + len - length of read buffer + + When reading a packet, this function looks for start of Kermit packet + (k->r_soh), then reads everything between it and the end of the packet + (k->r_eom) into the indicated buffer. Returns the number of bytes read, or: + 0 - timeout or other possibly correctable error; + -1 - fatal error, such as loss of connection, or no buffer to read into. +*/ + +int +readpkt(struct k_data * k, UCHAR *p, int len, int fc) { + int x, n, max; + short flag; + UCHAR c; +/* + Timeout not implemented in this sample. + It should not be needed. All non-embedded Kermits that are capable of + making connections are also capable of timing out, and only one Kermit + needs to time out. NOTE: This simple example waits for SOH and then + reads everything up to the negotiated packet terminator. A more robust + version might be driven by the value of the packet-length field. +*/ +#ifdef DEBUG + char * p2; +#endif /* DEBUG */ + +#ifdef F_CTRLC + short ccn; + ccn = 0; +#endif /* F_CTRLC */ + + if (ttyfd < 0 || !p) { /* Device not open or no buffer */ + debug(DB_MSG,"readpkt FAIL",0,0); + return(-1); + } + flag = n = 0; /* Init local variables */ + +#ifdef DEBUG + p2 = p; +#endif /* DEBUG */ + + while (1) { + x = getchar(); /* Replace this with real i/o */ + c = (k->parity) ? x & 0x7f : x & 0xff; /* Strip parity */ + +#ifdef F_CTRLC + /* In remote mode only: three consecutive ^C's to quit */ + if (k->remote && c == (UCHAR) 3) { + if (++ccn > 2) { + debug(DB_MSG,"readpkt ^C^C^C",0,0); + return(-1); + } + } else { + ccn = 0; + } +#endif /* F_CTRLC */ + + if (!flag && c != k->r_soh) /* No start of packet yet */ + continue; /* so discard these bytes. */ + if (c == k->r_soh) { /* Start of packet */ + flag = 1; /* Remember */ + continue; /* But discard. */ + } else if (c == k->r_eom /* Packet terminator */ + || c == '\012' /* 1.3: For HyperTerminal */ + ) { +#ifdef DEBUG + *p = NUL; /* Terminate for printing */ + debug(DB_PKT,"RPKT",p2,n); +#endif /* DEBUG */ + return(n); + } else { /* Contents of packet */ + if (n++ > k->r_maxlen) /* Check length */ + return(0); + else + *p++ = x & 0xff; + } + } + debug(DB_MSG,"READPKT FAIL (end)",0,0); + return(-1); +} + +/* T X _ D A T A -- Writes n bytes of data to communication device. */ +/* + Call with: + k = pointer to Kermit struct. + p = pointer to data to transmit. + n = length. + Returns: + X_OK on success. + X_ERROR on failure to write - i/o error. +*/ +int +tx_data(struct k_data * k, UCHAR *p, int n) { + int x; + int max; + + max = 10; /* Loop breaker */ + + while (n > 0) { /* Keep trying till done */ + x = write(ttyfd,p,n); + debug(DB_MSG,"tx_data write",0,x); + if (x < 0 || --max < 1) /* Errors are fatal */ + return(X_ERROR); + n -= x; + p += x; + } + return(X_OK); /* Success */ +} + +/* O P E N F I L E -- Open output file */ +/* + Call with: + Pointer to filename. + Size in bytes. + Creation date in format yyyymmdd hh:mm:ss, e.g. 19950208 14:00:00 + Mode: 1 = read, 2 = create, 3 = append. + Returns: + X_OK on success. + X_ERROR on failure, including rejection based on name, size, or date. +*/ +int +openfile(struct k_data * k, UCHAR * s, int mode) { + + switch (mode) { + case 1: /* Read */ + if (!(ifile = fopen(s,"r"))) { + debug(DB_LOG,"openfile read error",s,0); + return(X_ERROR); + } + k->s_first = 1; /* Set up for getkpt */ + k->zinbuf[0] = '\0'; /* Initialize buffer */ + k->zinptr = k->zinbuf; /* Set up buffer pointer */ + k->zincnt = 0; /* and count */ + debug(DB_LOG,"openfile read ok",s,0); + return(X_OK); + + case 2: /* Write (create) */ + ofile = creat(s,0644); + if (ofile < 0) { + debug(DB_LOG,"openfile write error",s,0); + return(X_ERROR); + } + debug(DB_LOG,"openfile write ok",s,0); + return(X_OK); + +#ifdef COMMENT + case 3: /* Append (not used) */ + ofile = open(s,O_WRONLY|O_APPEND); + if (ofile < 0) { + debug(DB_LOG,"openfile append error",s,0); + return(X_ERROR); + } + debug(DB_LOG,"openfile append ok",s,0); + return(X_OK); +#endif /* COMMENT */ + + default: + return(X_ERROR); + } +} + +/* F I L E I N F O -- Get info about existing file */ +/* + Call with: + Pointer to filename + Pointer to buffer for date-time string + Length of date-time string buffer (must be at least 18 bytes) + Pointer to int file type: + 0: Prevailing type is text. + 1: Prevailing type is binary. + Transfer mode (0 = auto, 1 = manual): + 0: Figure out whether file is text or binary and return type. + 1: (nonzero) Don't try to figure out file type. + Returns: + X_ERROR on failure. + 0L or greater on success == file length. + Date-time string set to yyyymmdd hh:mm:ss modtime of file. + If date can't be determined, first byte of buffer is set to NUL. + Type set to 0 (text) or 1 (binary) if mode == 0. +*/ +#ifdef F_SCAN +#define SCANBUF 1024 +#define SCANSIZ 49152 +#endif /* F_SCAN */ + +ULONG +fileinfo(struct k_data * k, + UCHAR * filename, UCHAR * buf, int buflen, short * type, short mode) { + struct stat statbuf; + struct tm * timestamp, * localtime(); + +#ifdef F_SCAN + FILE * fp; /* File scan pointer */ + char inbuf[SCANBUF]; /* and buffer */ +#endif /* F_SCAN */ + + if (!buf) + return(X_ERROR); + buf[0] = '\0'; + if (buflen < 18) + return(X_ERROR); + if (stat(filename,&statbuf) < 0) + return(X_ERROR); + timestamp = localtime(&(statbuf.st_mtime)); + sprintf(buf,"%04d%02d%02d %02d:%02d:%02d", + timestamp->tm_year + 1900, + timestamp->tm_mon + 1, + timestamp->tm_mday, + timestamp->tm_hour, + timestamp->tm_min, + timestamp->tm_sec + ); +#ifdef F_SCAN +/* + Here we determine if the file is text or binary if the transfer mode is + not forced. This is an extremely crude sample, which diagnoses any file + that contains a control character other than HT, LF, FF, or CR as binary. + A more thorough content analysis can be done that accounts for various + character sets as well as various forms of Unicode (UTF-8, UTF-16, etc). + Or the diagnosis could be based wholly or in part on the filename. + etc etc. Or the implementation could skip this entirely by not defining + F_SCAN and/or by always calling this routine with type set to -1. +*/ + if (!mode) { /* File type determination requested */ + int isbinary = 1; + fp = fopen(filename,"r"); /* Open the file for scanning */ + if (fp) { + int n = 0, count = 0; + char c, * p; + + debug(DB_LOG,"fileinfo scan ",filename,0); + + isbinary = 0; + while (count < SCANSIZ && !isbinary) { /* Scan this much */ + n = fread(inbuf,1,SCANBUF,fp); + if (n == EOF || n == 0) + break; + count += n; + p = inbuf; + while (n--) { + c = *p++; + if (c < 32 || c == 127) { + if (c != 9 && /* Tab */ + c != 10 && /* LF */ + c != 12 && /* FF */ + c != 13) { /* CR */ + isbinary = 1; + debug(DB_MSG,"fileinfo BINARY",0,0); + break; + } + } + } + } + fclose(fp); + *type = isbinary; + } + } +#endif /* F_SCAN */ + + return((ULONG)(statbuf.st_size)); +} + + +/* R E A D F I L E -- Read data from a file */ + +int +readfile(struct k_data * k) { + if (!k->zinptr) { +#ifdef DEBUG + fprintf(dp,"readfile ZINPTR NOT SET\n"); +#endif /* DEBUG */ + return(X_ERROR); + } + if (k->zincnt < 1) { /* Nothing in buffer - must refill */ + if (k->binary) { /* Binary - just read raw buffers */ + k->dummy = 0; + k->zincnt = fread(k->zinbuf, 1, k->zinlen, ifile); + debug(DB_LOG,"readfile binary ok zincnt",0,k->zincnt); + + } else { /* Text mode needs LF/CRLF handling */ + int c; /* Current character */ + for (k->zincnt = 0; (k->zincnt < (k->zinlen - 2)); (k->zincnt)++) { + if ((c = getc(ifile)) == EOF) + break; + if (c == '\n') /* Have newline? */ + k->zinbuf[(k->zincnt)++] = '\r'; /* Insert CR */ + k->zinbuf[k->zincnt] = c; + } +#ifdef DEBUG + k->zinbuf[k->zincnt] = '\0'; + debug(DB_LOG,"readfile text ok zincnt",0,k->zincnt); +#endif /* DEBUG */ + } + k->zinbuf[k->zincnt] = '\0'; /* Terminate. */ + if (k->zincnt == 0) /* Check for EOF */ + return(-1); + k->zinptr = k->zinbuf; /* Not EOF - reset pointer */ + } + (k->zincnt)--; /* Return first byte. */ + + debug(DB_LOG,"readfile exit zincnt",0,k->zincnt); + debug(DB_LOG,"readfile exit zinptr",0,k->zinptr); + return(*(k->zinptr)++ & 0xff); +} + + +/* W R I T E F I L E -- Write data to file */ +/* + Call with: + Kermit struct + String pointer + Length + Returns: + X_OK on success + X_ERROR on failure, such as i/o error, space used up, etc +*/ +int +writefile(struct k_data * k, UCHAR * s, int n) { + int rc; + rc = X_OK; + + debug(DB_LOG,"writefile binary",0,k->binary); + + if (k->binary) { /* Binary mode, just write it */ + if (write(ofile,s,n) != n) + rc = X_ERROR; + } else { /* Text mode, skip CRs */ + UCHAR * p, * q; + int i; + q = s; + + while (1) { + for (p = q, i = 0; ((*p) && (*p != (UCHAR)13)); p++, i++) ; + if (i > 0) + if (write(ofile,q,i) != i) + rc = X_ERROR; + if (!*p) break; + q = p+1; + } + } + return(rc); +} + +/* C L O S E F I L E -- Close output file */ +/* + Mode = 1 for input file, mode = 2 or 3 for output file. + + For output files, the character c is the character (if any) from the Z + packet data field. If it is D, it means the file transfer was canceled + in midstream by the sender, and the file is therefore incomplete. This + routine should check for that and decide what to do. It should be + harmless to call this routine for a file that that is not open. +*/ +int +closefile(struct k_data * k, UCHAR c, int mode) { + int rc = X_OK; /* Return code */ + + switch (mode) { + case 1: /* Closing input file */ + if (!ifile) /* If not not open */ + break; /* do nothing but succeed */ + debug(DB_LOG,"closefile (input)",k->filename,0); + if (fclose(ifile) < 0) + rc = X_ERROR; + break; + case 2: /* Closing output file */ + case 3: + if (ofile < 0) /* If not open */ + break; /* do nothing but succeed */ + debug(DB_LOG,"closefile (output) name",k->filename,0); + debug(DB_LOG,"closefile (output) keep",0,k->ikeep); + if (close(ofile) < 0) { /* Try to close */ + rc = X_ERROR; + } else if ((k->ikeep == 0) && /* Don't keep incomplete files */ + (c == 'D')) { /* This file was incomplete */ + if (k->filename) { + debug(DB_LOG,"deleting incomplete",k->filename,0); + unlink(k->filename); /* Delete it. */ + } + } + break; + default: + rc = X_ERROR; + } + return(rc); +} + +#ifdef DEBUG +int xerror() { + unsigned int x; + extern int errorrate; /* Fix this - NO EXTERNS */ + if (!errorrate) + return(0); + x = rand() % 100; /* Fix this - NO C LIBRARY */ + debug(DB_LOG,"RANDOM",0,x); + debug(DB_LOG,"ERROR",0,(x < errorrate)); + return(x < errorrate); +} +#endif /* DEBUG */ |