#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));
}