summaryrefslogtreecommitdiff
path: root/kermit.c
diff options
context:
space:
mode:
Diffstat (limited to 'kermit.c')
-rw-r--r--kermit.c1619
1 files changed, 1619 insertions, 0 deletions
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));
+}