home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Frozen Fish 1: Amiga
/
FrozenFish-Apr94.iso
/
bbs
/
alib
/
d1xx
/
d108
/
tek.lha
/
Tek
/
kermit.c
< prev
next >
Wrap
C/C++ Source or Header
|
1987-10-31
|
22KB
|
865 lines
/*************************************************************
* vt100 terminal emulator - KERMIT protocol support
*
* v2.6 870227 DBW - bug fixes for all the stuff in v2.5
* v2.5 870214 DBW - more additions (see readme file)
* v2.4 861214 DBW - lots of fixes/additions (see readme file)
* v2.3 861101 DBW - minor bug fixes
* v2.2 861012 DBW - more of the same
* v2.1 860915 DBW - new features (see README)
* 860901 ACS - Added eight bit quoting
* 860830 Steve Drew Wild card support, err recovry,bugs.
* 860823 DBW - Integrated and rewrote lots of code
* 860811 Steve Drew multi filexfer, bugs, status line ect..
* v2.0 860809 DBW - Major rewrite
* v1.1 860720 DBW - Switches, 80 cols, colors, bug fixes
* v1.0 860712 DBW - First version released
*
*************************************************************/
#include "vt100.h"
#define MAXPACKSIZ 94 /* Maximum msgpkt size */
#define CR 13 /* ASCII Carriage Return */
#define LF 10 /* ASCII line feed */
#define SP 32 /* ASCII space */
#define DEL 127 /* Delete (rubout) */
#define MAXTRY 5 /* Times to retry a msgpkt */
#define MYQUOTE '#' /* Quote character I will use */
#define MYRPTQ '~' /* Repeat quote character */
#define MYEBQ '&' /* 8th bit prefix character */
#define MYPAD 0 /* Number of padding charss I will need */
#define MYPCHAR 0 /* Padding character I need (NULL) */
#define MYEOL '\n' /* End-Of-Line character I need */
#define tochar(ch) ((ch) + ' ')
#define unchar(ch) ((ch) - ' ')
#define ctl(ch) ((ch) ^ 64 )
/* Global Variables */
int
size, /* Size of present data */
osize, /* Size of last data entry */
rpsiz, /* Maximum receive msgpkt size */
spsiz, /* Maximum send msgpkt size */
timint, /* Time interval to wait */
pad, /* How much padding to send */
n, /* Packet number */
tp, /* total packets */
numtry, /* Times this msgpkt retried */
retry, /* total retries */
oldtry, /* Times previous msgpkt retried */
sendabort, /* flag for aborting send file */
rptflg, /* are we doing repeat quoting */
ebqflg, /* are we doing 8th bit quoting */
notfirst, /* is this the first file received */
first, /* is this the first time in a file */
rpt, /* current repeat count */
next, /* what is the next character */
t; /* current character */
long
totbytes; /* total bytes xfered on this file */
char
state, /* Present state of the automaton */
padchar, /* Padding character to send */
eol, /* End-Of-Line character to send */
quote, /* Quote character in incoming data */
rptq, /* Quote character for repeats */
ebq, /* Quote character for 8th bit quoting */
ackpkt[MAXPACKSIZ+20], /* ACK/NAK packet buffer */
msgpkt[MAXPACKSIZ+20], /* Message Packet buffer */
filnam[40], /* remote file name */
snum[10],
sl1[] = "FILE PKT NUM RETR BYTES",
sl2[] = "%-15s %c %4d %2d %6ld %5s",
sl3[50],
mainmode[10];
FILE *fp; /* file for send/receive */
char *
getfname(name) /* returns ptr to start of file name from spec */
char *name;
{
int l;
l = strlen(name);
while(l && name[l] != '/' && name[l] != ':') l--;
if (name[l] == '/' || name[l] == ':') l++;
return(name += l);
}
doksend(file,more)
char *file;
int more;
{
int amount, c, wild;
char *p, **list = NULL;
if (!strcmp(file,"$")) { saybye(); return(2); }
p = file;
while(*p && *p != '*' && *p != '?') p++;
if (*p) {
wild = TRUE;
list = expand(file, &amount);
if (list == NULL) req("KERMIT","No wild card match",0);
}
else {
wild = FALSE;
amount = 1;
}
for (c = 0; c < amount; c++) {
if (wild == TRUE) p = list[c];
else p = file;
strcpy(filnam,getfname(p));
ttime = TTIME_KERMIT;
tp = retry = n = numtry = 0; totbytes = 0L;
if ((fp = fopen(p,"r")) == NULL) {
req("KERMIT: can't open send file:",p,0);
continue;
}
strcpy(mainmode,"SEND");
ClearBuffer();
if (sendsw()) dostats(' ',"DONE");
fclose(fp);
}
free_expand(list);
return TRUE;
}
dokreceive(file,more)
char *file;
int more;
{
int retval;
ttime = TTIME_KERMIT;
if (!strcmp(file,"$")) { saybye(); return(2); }
strcpy(filnam, file);
if (server) strcpy(mainmode,"GET");
else strcpy(mainmode,"RECV");
tp = retry = n = numtry = notfirst = 0; totbytes = 0L;
ClearBuffer();
retval = recsw();
return(retval);
}
sendsw()
{
char sinit(), sfile(), sdata(), seof(), sbreak();
sendabort = 0;
state = 'S';
while(TRUE) {
switch(state) {
case 'S': state = sinit(); break;
case 'F': state = sfile(); break;
case 'D': state = sdata(); break;
case 'Z': state = seof(); break;
case 'B': state = sbreak(); break;
case 'C': if (sendabort) return FALSE;
else return TRUE;
case 'E': dostats('E',"ERROR"); /* so print the err and abort */
print_host_err(ackpkt);
return(FALSE);
case 'A': if (timeout == USERABORT) {
timeout = GOODREAD;
n = (n+1)%64;
sendabort = 1;
dostats('A',"ABORT");
strcpy(msgpkt, "D");
state = 'Z';
break;
}
if (timeout == TIMEOUT) dostats('A',"TMOUT");
else { /* protocol error dectected by us */
dostats('A',"ERROR");
print_our_err();
}
return(FALSE);
default: return(FALSE);
}
}
}
char sinit()
{
int num, len;
retry++;
if (numtry++ > MAXTRY) return('A');
spar(msgpkt);
spack('S',n,9,msgpkt);
switch(rpack(&len,&num,ackpkt)) {
case 'N': return(state);
case 'Y': if (n != num) return(state);
rpar(ackpkt);
if (eol == 0) eol = '\n';
if (quote == 0) quote = MYQUOTE;
numtry = 0;
retry--;
n = (n+1)%64;
return('F');
case 'E': return('E');
case FALSE:if (timeout == USERABORT) state = 'A';
return(state);
default: return('A');
}
}
char sfile()
{
int num, len;
retry++;
if (numtry++ > MAXTRY) return('A');
spack('F',n,strlen(filnam),filnam);
switch(rpack(&len,&num,ackpkt)) {
case 'N':
num = (--num<0 ? 63:num);
if (n != num) return(state);
case 'Y':
if (n != num) return(state);
numtry = 0;
retry--;
n = (n+1)%64;
first = 1;
size = getpkt();
return('D');
case 'E':
return('E');
case FALSE: if (timeout == USERABORT) state = 'A';
return(state);
default:
return('A');
}
}
char sdata()
{
int num, len;
retry++;
if (numtry++ > MAXTRY) return('A');
spack('D',n,size,msgpkt);
switch(rpack(&len,&num,ackpkt)) {
case 'N':
num = (--num<0 ? 63:num);
if (n != num) return(state);
case 'Y':
if (n != num) return(state);
numtry = 0;
retry--;
n = (n+1)%64;
if ((size = getpkt()) == 0) return('Z');
return('D');
case 'E':
return('E');
case FALSE: if (timeout == USERABORT) state = 'A';
return(state);
default:
return('A');
}
}
char seof()
{
int num, len;
retry++;
if (numtry++ > MAXTRY) return('A');
/* if (timeout == USERABORT) {*/ /* tell host to discard file */
/* timeout = GOODREAD; */
/* spack('Z',n,1,"D"); */
/* } */
/* else */
spack('Z',n,sendabort,msgpkt);
switch(rpack(&len,&num,ackpkt)) {
case 'N':
num = (--num<0 ? 63:num);
if (n != num) return(state);
case 'Y':
if (n != num) return(state);
numtry = 0;
retry--;
n = (n+1)%64;
return('B');
case 'E':
return('E');
case FALSE: return(state);
default:
return('A');
}
}
char sbreak()
{
int num, len;
retry++;
if (numtry++ > MAXTRY) return('A');
spack('B',n,0,msgpkt);
switch (rpack(&len,&num,ackpkt)) {
case 'N':
num = (--num<0 ? 63:num);
if (n != num) return(state);
case 'Y':
if (n != num) return(state);
numtry = 0;
retry--;
n = (n+1)%64;
return('C');
case 'E':
return('E');
case FALSE: return(state);
default: return ('A');
}
}
/* timeout equals USERABORT so lets end the file and quit */
/* when host receives 'Z' packet with "D" in data field he */
/* should discard the file. */
/*
sabort()
{
dostats(' ',"ABORT");
n = (n+1)%64;
retry--;
state = 'Z';
while (state == 'Z') state = seof();
while (state == 'B') state = sbreak();
return(FALSE);
}
*/
recsw()
{
char rinit(), rfile(), rdata();
state = 'R';
while(TRUE) {
switch(state) {
case 'R': state = rinit(); break;
case 'Z':
case 'F': state = rfile(); break;
case 'D': state = rdata(); break;
case 'C': return(TRUE);
case 'E':
case 'A': /* easy way to cleanly abort
should really send and ACK
with "X" in data field and
wait for host to abort but
not all kermits support
this feature. */
if (timeout == USERABORT){
/* send an error packet back */
dostats('A',"ABORT");
spack('E',n,12,"User aborted");
}
else if (timeout == TIMEOUT) {
/* we timed out waiting */
/* will we need to spack here ?*/
dostats('A',"TMOUT");
}
/* must be 'E' from host or we
detected a protocol error */
else dostats('A',"ERROR");
if (state == 'E') print_host_err(msgpkt);
else if (timeout == GOODREAD) /* tell host why */
print_our_err();
/* will this kill all files ?*/
do {
ttime = 2;
readchar();
} while (timeout == GOODREAD);
fclose(fp);
sendstring("\r");
return(FALSE);
default: return(FALSE);
}
}
}
char rinit()
{
int len, num;
retry++;
if (numtry++ > MAXTRY) return('A');
if (server) spack('R',n,strlen(filnam),filnam);
else spack('N',n,0,0);
switch(rpack(&len,&num,msgpkt)) {
case 'S':
rpar(msgpkt);
spar(msgpkt);
spack('Y',n,9,msgpkt);
oldtry = numtry;
numtry = 0;
retry--;
n = (n+1)%64;
return('F');
case 'E':
return('E');
case FALSE:
if (timeout == USERABORT) return('A');
spack('N',n,0,0);
return(state);
default:
return('A');
}
}
char rfile()
{
int num, len;
retry++;
if (numtry++ > MAXTRY) return('A');
switch(rpack(&len,&num,msgpkt)) {
case 'S':
if (oldtry++ > MAXTRY) return('A');
if (num == ((n==0) ? 63:n-1)) {
spar(msgpkt);
spack('Y',num,9,msgpkt);
numtry = 0;
return(state);
}
else return('A');
case 'Z':
if (oldtry++ > MAXTRY) return('A');
if (num == ((n==0) ? 63:n-1)) {
spack('Y',num,0,0);
numtry = 0;
return(state);
}
else return('A');
case 'F':
if (num != n) return('A');
strcpy(filnam,msgpkt);
if (p_convert) {
char *p;
p = &filnam[0];
while (*p) { *p = tolower(*p); p++; }
}
if (notfirst) {
totbytes = 0L;
dostats('F',"RECV");
}
/* is the first file so emit actual file name from host */
else {
notfirst++;
}
if ((fp = fopen(filnam,"w")) == NULL) {
req("KERMIT: Unable to create file:",filnam,0);
strcpy(msgpkt,"VT100 - Kermit - cannot create file: ");
strcat(msgpkt,filnam);
spack('E',n,strlen(msgpkt),msgpkt); /* let host know */
dostats('E',"ERROR");
return ('\0'); /* abort everything */
}
spack('Y',n,0,0);
oldtry = numtry;
numtry = 0;
retry--;
n = (n+1)%64;
return('D');
/* totaly done server sending no more */
case 'B':
if (num != n) return ('A');
spack('Y',n,0,0);
retry--;
return('C');
case 'E':
return('E');
case FALSE:
if (timeout == USERABORT) return('A');
spack('N',n,0,0);
return(state);
default:
return ('A');
}
}
char rdata()
{
int num, len;
retry++;
if (numtry++ > MAXTRY) return('A');
switch(rpack(&len,&num,msgpkt)) {
case 'D':
if (num != n) {
if (oldtry++ > MAXTRY) return('A');
if (num == ((n==0) ? 63:n-1)) {
spack('Y',num,6,msgpkt);
numtry = 0;
return(state);
}
else return('A');
}
decode();
spack('Y',n,0,0);
oldtry = numtry;
numtry = 0;
retry--;
n = (n+1)%64;
return('D');
case 'Z':
if (num != n) return('A');
spack('Y',n,0,0);
n = (n+1)%64;
retry--;
dostats('Z',"DONE");
fclose(fp);
return('Z');
case 'F':
if (oldtry++ > MAXTRY) return('A');
if (num == ((n==0) ? 63:n-1)) {
spack('Y',num,0,0);
numtry = 0;
return(state);
}
case 'E':
return('E');
case FALSE:
if (timeout == USERABORT) return('A');
spack('N',n,0,0);
return(state);
default:
return('A');
}
}
spack(type,num,len,data)
char type, *data;
int num, len;
{
int i;
char chksum, buffer[100];
register char *bufp;
dostats(type,mainmode);
bufp = buffer;
ClearBuffer();
for (i=1; i<=pad; i++) sendchar(padchar);
*bufp++ = SOH;
*bufp++ = tochar(len+3);
chksum = tochar(len+3);
*bufp++ = tochar(num);
chksum += tochar(num);
*bufp++ = type;
chksum += type;
for (i=0; i<len; i++) {
*bufp++ = data[i];
chksum += data[i];
}
chksum = (((chksum&0300) >> 6)+chksum)&077;
*bufp++ = tochar(chksum);
*bufp++ = '\r';
*bufp++ = '\n';
*bufp = 0;
sendstring(buffer);
}
rpack(len,num,data)
int *len, *num;
char *data;
{
int i, done;
char type, cchksum, rchksum;
char t = '\0';
do {
t = readchar();
if (timeout != GOODREAD) return(FALSE);
} while (t != SOH);
done = FALSE;
while (!done) {
t = readchar();
if (timeout != GOODREAD) return(FALSE);
if (t == SOH) continue;
cchksum = t;
*len = unchar(t)-3;
t = readchar();
if (timeout != GOODREAD) return(FALSE);
if (t == SOH) continue;
cchksum = cchksum + t;
*num = unchar(t);
t = readchar();
if (timeout != GOODREAD) return(FALSE);
if (t == SOH) continue;
cchksum = cchksum + t;
type = t;
for (i=0; i<*len; i++) {
t = readchar();
if (timeout != GOODREAD) return(FALSE);
if (t == SOH) continue;
cchksum = cchksum + t;
data[i] = t;
}
data[*len] = 0;
t = readchar();
if (timeout != GOODREAD) return(FALSE);
rchksum = unchar(t);
t = readchar();
if (timeout != GOODREAD) return(FALSE);
if (t == SOH) continue;
done = TRUE;
}
dostats(type,mainmode);
cchksum = (((cchksum&0300) >> 6)+cchksum)&077;
if (cchksum != rchksum) return(FALSE);
return((int)type);
}
getpkt() {
int i,eof;
static char leftover[10] = { '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0' };
if (first == 1) {
first = 0;
*leftover = '\0';
t = getc(fp);
if (t == EOF) {
first = 1;
return(size = 0);
}
totbytes++;
}
else if (first == -1) {
first = 1;
return(size = 0);
}
for (size = 0; (msgpkt[size] = leftover[size]) != '\0'; size++) ;
*leftover = '\0';
rpt = 0;
eof = 0;
while (!eof) {
next = getc(fp);
if (next == EOF) {
first = -1;
eof = 1;
}
else totbytes++;
osize = size;
encode(t);
t = next;
if (size == spsiz-3) return(size);
if (size > spsiz-3) {
for (i = 0; (leftover[i] = msgpkt[osize+i]) != '\0'; i++) ;
size = osize;
msgpkt[size] = '\0';
return(size);
}
}
return(size);
}
void encode(a)
char a;
{
int a7,b8;
if (p_mode == 1 && a == '\n') {
rpt = 0;
msgpkt[size++] = quote;
msgpkt[size++] = ctl('\r');
if (size <= spsiz-3) osize = size;
msgpkt[size++] = quote;
msgpkt[size++] = ctl('\n');
msgpkt[size] = '\0';
return;
}
if (rptflg) {
if (a == next && (first == 0)) {
if (++rpt < 94) return;
else if (rpt == 94) {
msgpkt[size++] = rptq;
msgpkt[size++] = tochar(rpt);
rpt = 0;
}
}
else if (rpt == 1) {
rpt = 0;
encode(a);
if (size <= spsiz-3) osize = size;
rpt = 0;
encode(a);
return;
}
else if (rpt > 1) {
msgpkt[size++] = rptq;
msgpkt[size++] = tochar(++rpt);
rpt = 0;
}
}
a7 = a & 0177;
b8 = a & 0200;
if (ebqflg && b8) { /* Do 8th bit prefix if necessary. */
msgpkt[size++] = ebq;
a = a7;
}
if ((a7 < SP) || (a7==DEL)) {
msgpkt[size++] = quote;
a = ctl(a);
}
if (a7 == quote) msgpkt[size++] = quote;
if ((rptflg) && (a7 == rptq)) msgpkt[size++] = quote;
if ((ebqflg) && (a7 == ebq)) /* Prefix the 8th bit prefix */
msgpkt[size++] = quote; /* if doing 8th-bit prefixes */
msgpkt[size++] = a;
msgpkt[size] = '\0';
}
void decode()
{
USHORT a, a7, b8;
char *buf;
buf = msgpkt;
rpt = 0;
while ((a = *buf++) != '\0') {
if (rptflg) {
if (a == rptq) {
rpt = unchar(*buf++);
a = *buf++;
}
}
b8 = 0;
if (ebqflg) { /* 8th-bit prefixing? */
if (a == ebq) { /* Yes, got an 8th-bit prefix? */
b8 = 0200; /* Yes, remember this, */
a = *buf++; /* and get the prefixed character. */
}
}
if (a == quote) {
a = *buf++;
a7 = a & 0177;
if ((a7 >= 0100 && a7 <= 0137) || a7 == '?') a = ctl(a);
}
a |= b8;
if (rpt == 0) rpt = 1;
if (p_mode == 1 && a == '\r') continue;
totbytes += rpt;
for (; rpt > 0; rpt--) putc(a, fp);
}
return;
}
void spar(data)
char data[];
{
data[0] = tochar(MAXPACKSIZ);
data[1] = tochar(TTIME_KERMIT);
data[2] = tochar(MYPAD);
data[3] = ctl(MYPCHAR);
data[4] = tochar(MYEOL);
data[5] = MYQUOTE;
if ((p_parity > 0) || ebqflg) { /* 8-bit quoting... */
data[6] = MYEBQ; /* If parity or flag on, send &. */
if ((ebq > 0040 && ebq < 0100) || /* If flag off, then turn it on */
(ebq > 0140 && ebq < 0177) || /* if other side has asked us to */
(ebq == 'Y')) ebqflg = 1;
}
else /* Normally, */
data[6] = 'Y'; /* just say we're willing. */
data[7] = '1';
data[8] = MYRPTQ;
data[9] = '\0';
}
void rpar(data)
char data[];
{
spsiz = unchar(data[0]);
ttime = unchar(data[1]);
pad = unchar(data[2]);
padchar = ctl(data[3]);
eol = unchar(data[4]);
quote = data[5];
rptflg = 0;
ebqflg = 0;
if (data[6] == 0) return;
ebq = data[6];
if ((ebq > 040 && ebq < 0100) ||
(ebq > 0140 && ebq < 0177)) ebqflg = 1;
else if (((p_parity > 0) || ebqflg) && (ebq == 'Y')) {
ebqflg = 1;
ebq = '&';
}
else ebqflg = 0;
if (data[7] == 0) return;
if (data[8] == 0) return;
rptq = data[8];
rptflg = ((rptq > 040 && rptq < 0100) ||
(rptq > 0140 && rptq < 0177));
}
saybye()
{
int len,num;
spack('G',n,1,"F"); /* shut down server no more files */
rpack(&len,&num,ackpkt);
}
print_our_err()
{
if (retry > MAXTRY || oldtry > MAXTRY) {
req("KERMIT:","Too may retries for packet",0);
strcpy(msgpkt,"VT100 KERMIT: Too many retries for packet");
}
else {
req("KERMIT:","Protocol Error",0);
strcpy(msgpkt,"VT100 KERMIT: Protocol Error");
}
spack('E',n,strlen(msgpkt));
}
print_host_err(msg)
char *msg;
{
req("KERMIT Host Error:",msg,0);
}
dostats(type,stat)
char type,*stat;
{
if (type == 'Y' || type == 'N' || type == 'G') return;
sprintf(sl3,sl2,filnam,type,n+(tp*64),retry-1,(long)totbytes,stat);
if (n==63) tp++;
req(sl1,sl3,0);
}
ClearBuffer()
{
AbortIO(Read_Request);
Read_Request->IOSer.io_Command = CMD_CLEAR;
DoIO(Read_Request);
Read_Request->IOSer.io_Command = CMD_READ;
SendIO(Read_Request);
}