home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Columbia Kermit
/
kermit.zip
/
archives
/
uclkermit.zip
/
cuclker.c
< prev
next >
Wrap
C/C++ Source or Header
|
1988-08-16
|
66KB
|
1,599 lines
/* File uclk15.c on ucl u44d - UCL Kermit main source file;
Chris Kennington. 30th September 1985 */
/* This is a remote-only Kermit for Berkeley Unix 2.8 (11/44),
produced at UCL by surgery on the "demonstration" Kermit,
plus enhancements.
The program should run unmodified in any unix-like environment;
for use in any other environment in which an ASCII OS supports
a VDU terminal on an RS232 port, there should be no need to
modify any routines in this file. All possibly machine-dependent
routines have been isolated in file "uclkmd.c"; these should
be checked before compilation.
8th-bit prefixing is specified by a "8" flag on the command-line;
"7" forces 7-bit, "i" forces 8-bit image. If no such flag, then
7-bit is assumed. Repeat-prefixing is always potentially on.
In 7-bit mode only: LF is converted to CR/LF in transmission
and CR, LF, CR/LF or LF/CR to LF on receipt; this code is
in the machine-dependent routines.
Chris Kennington. UCL. 9th May 1985. */
/* The modification history of this program is in file uclkmods.dox */
/* Identification string: */
char ident[] ="UCL Remote-only Kermit, V15B, March 1986.\n";
#include <stdio.h> /* Standard UNIX definitions */
#include <ctype.h> /* isascii definitions */
/* MAX & MIN values accepted */
#define MAXPACKSIZ 94 /* Maximum packet size */
#define MAXTIM 60 /* Maximum timeout interval */
#define MAXTRY 10 /* Times to retry a packet */
#define MINTIM 2 /* Minumum timeout interval */
/* My values to send to other end */
#define MYEOL '\n' /* End-Of-Line character I need */
#define MYPAD 0 /* Number of padding characters I will need */
#define MYPCHAR 0 /* Padding character I need (NULL) */
#define MYQUOTE '#' /* Quote character I will use */
#define MYTIME 10 /* Seconds after which I should be timed out */
/* Boolean constants */
#define TRUE -1
#define FALSE 0
/* ASCII Chars */
#define LF 10
#define CR 13 /* ASCII Carriage Return */
#define DEL 127 /* Delete (rubout) */
#define ESC 0x1b /* real escape char */
#define SOH 1 /* Start of header */
#define SP 32 /* ASCII space */
/* Macro Definitions */
/*
* tochar: converts a control character to a printable one by adding a space.
* unchar: undoes tochar.
* ctl: converts between control characters and printable characters by
* toggling the control bit (ie. ^A becomes A and A becomes ^A).
*/
#define tochar(ch) ((ch) + ' ')
#define unchar(ch) ((ch) - ' ')
#define ctl(ch) ((ch) ^ 64 )
#define forever while(1)
/* procedures passed as arguments etc.:- */
extern char cread();
extern char rinit(), rfile(), rdata(); /* receive procedures */
extern char sinit(), sfile(), sdata(), seof(), sbreak(); /* send procs */
extern int outfc(), outfile();
/* procedures in system-dependent file uclkmd.c:- */
extern char ascout(), ascedit(), filerr(), nextin();
extern int flushinput(), cooktty(), rawtty(), timoset(), unbuffer();
/* Global Variables */
int
aflg, rflg, sflg, /* flags for AUTO, RECEIVE, SEND */
debug, /* indicates level of debugging output (0=none) */
filnamcnv, /* -1 means do file name case conversions */
filecount, /* Number of files left to send */
image, oimage, /* 0 = 7-bit, 1 = 8-bit mode, 2 = prefixing */
n, /* Packet number */
oldt, /* previous char in encode() */
numtry, /* Times this packet retried */
oldtry, /* Times previous packet retried */
pad, /* How much padding to send */
qu8,
rpt,
rptflg,
sz,
size, /* Size of present data */
spsiz, /* Maximum send packet size */
timint = 10; /* Timeout for foreign host on sends */
char
c, /* dummy char */
*dname, /* name of debug file UCL */
*dt, /* data-block code/decode */
eol = '\n', /* End-Of-Line character to send */
**filelist, /* List of files to be sent */
*filenames[11],
*filnam, /* Current file name */
getfiles[MAXPACKSIZ],
iflg = 0, /* 8th-bit-mode set */
packet[MAXPACKSIZ], /* Packet buffer */
padchar, /* Padding character to send */
quote = '#', /* Quote character in incoming data */
recpkt[MAXPACKSIZ], /* Receive packet buffer */
state, /* Present state of the automaton */
timflag, /* timeout flag UCL */
type; /* received-packet type */
char amauto[] = "%sI am in automatic-server mode!";
char badpack[] = "%sYour Kermit sent invalid packet, type \"%c\"!";
char crlf[] = "\r\n";
char *eighths[] = {"7-bit character", "binary-image-mode", "8th-bit prefixed"};
char goodbye[] = "%sReturning to Unix; goodbye.";
char *logicval[]= {"OFF", "ON"};
char noname[] = "%sNo valid names in GET request";
char null[] = ""; /* null string */
char onlyg[] = "%sOnly BYE, LOGOUT, FINISH commands implemented";
char prompt[] = "KmUCL: ";
FILE *fp, /* current disk file */
*dfp; /* debug file UCL */
/*
* m a i n
* Main routine - parse command and options, set up the
* tty lines, and dispatch to the appropriate routine.
*/
main(argc,argv)
int argc; /* Character pointers to and count of */
char **argv; /* command line arguments */
{
char *cp, **argv1;
static char first = 0;
int argc1;
if (first != 0) {
printmsg("Program fault - reentered.\n");
exit(1);
}
else
++first;
unbuffer(); /* unbuffer output */
printf(ident);
aflg = sflg = rflg = 0; /* Turn off all parse flags */
cp = *++argv;
argv1 = argv; /* remember argument pointers */
argv++;
argc1 = --argc;
--argc; /* pointers to args */
/* Initialize these values and hope the first packet will get across OK */
eol = CR; /* EOL for outgoing packets */
quote = '#'; /* Standard control-quote char "#" */
pad = 0; /* No padding */
padchar = NULL; /* Use null if any padding wanted */
spsiz = MAXPACKSIZ;
qu8 = 0;
image = -1;
rptflg = TRUE; /* try for repeating */
filnamcnv = TRUE; /* conversion for UNIX systems */
dfp = 0;
dname = 0; /* clear debug file ptrs */
if (argc >= 0) while ((*cp) != NULL) switch (*cp++) {
/* If command-line, parse characters in first arg. */
case 'a':
aflg++; break; /* A = Auto command */
case 'f':
filnamcnv = FALSE; /* F = don't do case conversion */
break; /* on filenames */
case 'h':
debug = 0; /* there won't be a file yet */
help(); /* H = help messages */
case 'i': /* I = Image (8-bit) mode */
image = 1;
++iflg;
break;
case 'r':
rflg++; break; /* R = Receive command */
case 's':
sflg++; break; /* S = Send command */
case '7': /* 7-bit transfer */
image = 0;
++iflg;
break;
case '8': /* 8-bit prefixed */
image = 2;
++iflg;
break;
case '-': /* - flag (ignored) */
break;
default:
--cp;
printmsg("Invalid char %c in command-line.",*cp);
usage();
}
if (image == -1)
image = 0; /* default is 7-bit */
oimage = image; /* remember setting */
/* Flags parsed, check for debug filename */
cp = *argv;
if (*cp == '-') {
if (*++cp != 'D') { /* invalid debug filename */
printmsg("Debug filename <%s> must start with \"D\".",*--cp);
usage();
}
while (*cp == 'D') { /* count the Ds */
++debug;
++cp;
}
dname = cp; /* filename starts after Ds */
dfp = fopen(dname,"a");
++argv;
--argc;
}
if ((c = aflg+sflg+rflg) > 1) { /* Only one command allowed */
printmsg("One only of Auto OR Receive OR Send!");
usage();
}
if (iflg > 1) {
printmsg("One only of \"i\" or \"7\" !");
usage();
}
printmsg("%s transfer requested;",eighths[image]);
if (image != 0)
printf(" CR <=> LF translation will NOT be done.\n");
if (c == 0) { /* no action-flag */
printf(crlf);
printmsg("No action on command-line; Automatic mode assumed;\n For help use \"kermit h\".");
aflg = 1;
}
/* Put the tty into the correct mode */
rawtty();
/* All set up, now execute the command that was given. */
if (debug) {
if (dfp != 0) {
printmsg("Debugging level = %d into file \"%s\";",debug,dname);
fprintf(dfp,"\n\n******** UCL Kermit Debug File ** next run starts here **********\n");
fprintf(dfp,ident);
fprintf(dfp,"\nDebugging level = %d into file \"%s\";\nCommand-line: <",debug,dname);
while (argc1-- > 0)
fprintf(dfp," %s",*argv1++);
fprintf(dfp," >;");
if (aflg) fprintf(dfp,"\nAuto Command\n");
if (sflg) fprintf(dfp,"\nSend Command\n");
if (rflg) fprintf(dfp,"\nReceive Command\n");
}
else {
printf("No valid debug file, debug switched off.\r\n");
debug = 0;
}
}
if (sflg) /* Send command */
{
if (argc--) {
printmsg("Send Command; set your Kermit to receive files\n");
filnam = *argv++; /* Get file to send */
}
else {
cooktty(); /* restore tty to normal */
printmsg("Send, but no filename given;\n");
usage(); /* and give error */
}
fp = NULL; /* Indicate no file open yet */
filelist = argv; /* Set up the rest of the file list */
filecount = argc; /* Number of files left to send */
printmsg("I'll give you 10 seconds to get ready ...");
timoset(10);
while (timflag != 0)
c = cread(); /* hang about */
if (sendsw() == FALSE) /* Send the file(s) */
printmsg("Send failed."); /* Report failure */
else /* or */
printmsg("done."); /* success */
}
else if (rflg) { /* Receive command */
printmsg("Receive Command; set your Kermit to Send files\n");
printmsg("I'll give you 5 seconds to get ready ...");
timoset(5);
while (timflag != 0)
c = cread(); /* hang about */
timint = 20;
if (recsw() == FALSE) /* Receive the file(s) */
printmsg("Receive failed.");
else /* Report failure */
printmsg("done."); /* or success */
}
else while (aflg) { /* automatic (sub-server) mode */
printmsg("Auto Command; set your Kermit to Send or Get files;");
printf("\tto cancel, use \"LOGOUT\", \"BYE\" or \"FINISH\" command,\r\n");
printf("\t(or send ESCAPE-C at any time).\r\n");
if (autosw() == TRUE)
printmsg("Auto Send/Get complete");
else
printmsg("Auto-mode cancelled");
}
closeall();
cooktty();
exit(0);
} /* End main() */
autosw() /* state switcher for automatic mode */
{
int len, num;
char c, *wild;
forever {
timint = 40; /* slow NAKs at first */
if (debug)
fprintf(dfp,"\nAutosw() waiting: 8th-bit setting now %d, char is %c; reptflag %s.\n",image,qu8,logicval[-rptflg]);
n = numtry = 0;
switch ( (type = rpack(&len,&num,recpkt)) ) {
/* decipher request from micro */
case 'S': /* send-init */
/* receive file(s) from local Kermit */
state = 'F';
rpar(recpkt,len);
len = spar(packet);
spack('Y',n,len,packet); /* ack parameters */
n = (n+1)%64;
while (state != 'C') { /* until EoF */
if (debug == 1) fprintf(dfp," autosw state %c\n",state);
switch(state) {
case 'F': state = rfile(); break;
case 'D': state = rdata(); break;
case 'A': state = 'C'; break;
case 'C': break;
} } /* end switch, while */
image = oimage;
break; /* end of reception */
case 'R': /* get command */
/* send file(s) to micro Kermit */
if (debug) fprintf(dfp,"\n getting files <%s>, ", recpkt);
movemem(recpkt,getfiles,len); /* file name(s) */
getfiles[len] = 0;
wild = getfiles;
while ( (c = (*wild++)&0x7f) != 0 )
if ( (c == '*') || (c == '?') ) {
error("Cannot accept wildcard <%c> in \"GET\"",c);
break; /* no wildcard processing */
}
filecount = decol8(getfiles,filenames,10);
filelist = filenames;
if (debug > 1) {
fprintf(dfp," %d files, ",filecount);
while (*filelist != 0)
fprintf(dfp,"<%s> ",*filelist++);
filelist = filenames;
}
if (filecount--) { /* if any valid names */
filnam = *filelist++;
fp = NULL;
if (sendsw() == FALSE) /* Send the file(s) */
printmsg("Send failed."); /* Report failure */
else
printmsg("Files sent; still in auto-mode.");
}
else /* if no names */
error(noname,prompt);
image = oimage;
break;
case 'G': /* generic command */
/* only "BYE" and "LOGOUT" are acceptable */
if ( ( (c = *recpkt&0x5f) == 'F' ) || (c == 'L') ) {
error(goodbye,prompt);
aflg = 0;
return(TRUE);
}
else
error(onlyg,prompt);
break;
case 'I': /* info-exchange packet */
rpar(recpkt,len);
len = spar(packet);
spack('Y',n,len,packet); /* ack the parameters */
break;
case 'E': /* error packet */
prerrpkt(recpkt);
aflg = 0;
error(goodbye,prompt);
return(FALSE);
case 'C': /* OS command */
error(onlyg,prompt);
break;
case 'N': /* NAK - out of phase */
error(amauto,prompt);
break;
case FALSE: /* timeout - micro not sending */
printf(crlf);
printmsg("Please enter \"SEND\", \"GET\" or \"BYE\" command to micro Kermit,");
printf("\t(or send ESCAPE-C to cancel UCL Kermit) ...\n");
spack('N',0,0,null);
break;
default: /* bad packet */
error(badpack,prompt,type);
return(FALSE);
} } /* end switch, forever */
} /* End autosw() */
bufill(buffer)
/* Get a bufferful of data from the file that's being sent */
char *buffer;
{
int t;
static int softeof= FALSE;
if (softeof == TRUE) {
softeof = FALSE;
return(EOF);
}
rpt = sz = 0; /* Inits for encode() */
dt = buffer;
oldt = -2; /* impossible last char */
while((t = getc(fp)) != EOF) { /* next character */
t = ascedit(t); /* edited if necessary */
encode(t); /* to buffer */
if (sz >= spsiz-6) /* Check length */
return(sz);
}
/* reach here on (hard) EOF or error */
if (sz==0)
return(EOF);
else {
softeof = TRUE; /* EOF next time */
return(sz);
}
} /* end of bufill() */
closeall() /* close both files */
{
if (fp != NULL)
fclose(fp);
if (dfp != NULL) {
fprintf(dfp,"\n\n************************* End of Run ******************************\n\n");
fclose(dfp);
}
} /* end of closeall() */
char cread() /* get next char from line */
/* UCL Input Routine: handle escapes as well,
return SOH & printables, eat all other controls. */
{
char c, t;
int ex;
ex = 0;
while (ex == 0) {
t = nextin(); /* get next char */
if (timflag == 0)
return(0); /* timeout occured */
c = t&0x7f;
if (image != 1)
t = c;
if ( (c == CR) || (c == LF) || (c == SOH) || (c > 0x1f) )
return(t);
/* left only with invalid controls */
if (c == ESC) { /* process escape */
printf("\nKmUCL-ESC");
Repeat: /* for ignoring "-" */
read (0, &t, 1);
putchar(t);
c = t&0x5f;
switch(c) {
case SOH:
return(t);
case 'C':
cooktty();
printf("\nUCL Kermit exitting by ESC-C from local station\n");
closeall();
exit(1);
case 'H':
printf("\n\rUCL Kermit is alive and well ....\n\r");
break;
case 0x1f: /* ? */
case 0x0f: /* / */
cooktty();
help1();
rawtty();
break;
case '-':
goto Repeat;
default:
printf("???");
break;
} } }
return(0);
} /* End of cread() */
decode(buf,len) /* packet decoding procedure */
/* Called with string to be decoded; Returns 0 or error-code. */
char *buf;
int len;
{
char a, a7, b8, *end, rep;
int flg8=0, error=0, r;
if (image == 2)
flg8 = -1;
end = buf + len;
while (buf < end) {
a = *buf++;
if ( rptflg && (a == '~') ) { /* got a repeat prefix? */
rep = unchar(*buf++); /* Yes, get the repeat count, */
a = *buf++; /* and get the prefixed character. */
}
else
rep = 1;
b8 = 0; /* Check high order "8th" bit */
if ( flg8 && (a == qu8) ) {
b8 = 0200;
a = *buf++; /* and get the prefixed character. */
}
if (a == quote) { /* If control prefix, */
a = *buf++; /* get its operand. */
a7 = a & 0177; /* Only look at low 7 bits. */
if ((a7 >= 0100 && a7 <= 0137) || a7 == '?') /* Uncontrollify */
a = ctl(a); /* if in control range. */
}
a |= b8; /* OR in the 8th bit */
while (rep-- > 0) {
if (image == 0) /* if 7-bit */
r = ascout(a);
else /* prefixing / image */
r = putc(a,fp);
if (r == EOF) /* if error */
error |= filerr();
} }
return(error);
} /* end of decode() */
decol8(line,arr,num) /* decollate command-line */
/* Splits up line into sections delimited by controls or blanks,
zeros all such chars & places start-addresses into array,
up to maximum of num entries; zeros rest of entries and
returns count of valid entries. */
char *line, *arr[], num;
{
char c, count, *start;
int i, j;
j = count = 0;
start = line;
for (i=0; i<80, j<num; ++i) {
if ( (c = line[i]) <= SP) { /* terminator */
line[i] = 0;
if (count > 0) {
arr[j++] = start;
count = 0;
}
if (c == 0)
break; /* out of for */
}
else if (count++ == 0) /* printable */
start = &line[i]; /* start next parm */
} /* end else, for */
line[i] = 0; /* terminate last parm */
i = j; /* number of parms */
while (j < num)
arr[j++] = 0; /* clear garbage */
return(i);
} /* End of decol8() */
encode(a)
/* encode single character into packet for transmission */
int a; /* char to be encoded */
{
int a7; /* Low order 7 bits */
int b8; /* 8th bit of character */
int flg8 = 0;
static int oldsz;
if (image == 2)
flg8 = -1;
if (rptflg) { /* repeat-count processing */
if (a == oldt) { /* char is same */
/* This algorithm is simple but relatively inefficient; it stores
the repeat flag, count and character each time around so
that when the run is broken the buffer is valid; also it
treats a pair as a run, which requires 3 bytes not 2 unless
the pair is control- or 8bit-prefixed; but it does not
require lookahead logic from the data-read. */
sz = oldsz; /* wind back pointer */
dt[sz++] = '~'; /* store prefix */
dt[sz++] = tochar(++rpt); /* & count */
if (rpt > 93) /* force new start */
oldt = -2; /* impossible value */
}
else { /* not run, or end */
rpt = 1;
oldt = a; /* save char */
oldsz = sz;
} }
a7 = a & 0177; /* Isolate ASCII part */
b8 = a & 0200; /* and 8th (parity) bit. */
if (flg8 && b8) { /* Do 8th bit prefix if necessary. */
dt[sz++] = qu8;
a = a7;
}
if ((a7 < SP) || (a7==DEL)) { /* Do control prefix if necessary */
dt[sz++] = MYQUOTE;
a = ctl(a);
}
if (a7 == MYQUOTE) /* Prefix the control prefix */
dt[sz++] = MYQUOTE;
else if (rptflg && (a7 == '~')) /* If it's the repeat prefix, */
dt[sz++] = MYQUOTE; /* quote it if doing repeat counts. */
else if (flg8 && (a7 == qu8)) /* Prefix the 8th bit prefix */
dt[sz++] = MYQUOTE; /* if doing 8th-bit prefixes */
dt[sz++] = a; /* Finally, insert the character */
dt[sz] = '\0'; /* itself, and mark the end. */
return;
} /* end of encode() */
/*VARARGS1*/
error(fmt, a1, a2, a3, a4, a5)
/* Remote; send an error packet with the message.
* and if debug log in debug file */
char *fmt;
{
char msg[100];
int len;
sprintf(msg,fmt,a1,a2,a3,a4,a5); /* Make it a string */
len = strlen(msg);
spack('E',n,len,msg); /* Send the error packet */
if (debug) fprintf(dfp,msg);
return;
} /* End of error() */
gnxtfl() /* Get next file in a file group */
{
if (debug) fprintf(dfp,"\n gnxtfl: filelist = \"%s\"",*filelist);
filnam = *(filelist++);
if (filecount-- == 0)
return FALSE; /* If no more, fail */
else
return TRUE; /* else succeed */
} /* End gnxtfl() */
help() /* UCL help routine */
{
printf("\nSummary of UCL Remote Kermit:\n");
help1();
printf("\t\t\t\t hit CR for more ");
timoset(30);
while (timflag != 0)
if (cread() != 0)
break;
printf("\nBasic syntax of the \"kermit\" command is:\n");
usage(); /* exits */
} /* End of help() */
help1()
{
printf("\nThis is a remote receive-or-send-or-auto Kermit; normally called\n");
printf(" with a system command-line, failing which it enters automatic mode.\n");
printf("Image-mode transfers may be selected by \"i\"-flag, 8-bit-prefixed transfers\n");
printf(" by \"8\"-flag, otherwise 7-bit data only will be transferred;\n");
printf(" mapping between LFs and CR/LF pairs is only done during 7-bit transfers.\n");
printf("Repeat-prefixing is always available; there is NO Connect facility.\n");
printf("Automatic mode supports the Server-Kermit commands \"SEND\", \"GET\"\n");
printf(" and \"LOGOUT/BYE/QUIT\".\n");
printf("If your transfer breaks down, go into connect-mode and enter\n");
printf(" ESCAPE-C, which will cause UCL Kermit to quit gracefully;\n");
printf(" alternatively enter ESCAPE-H, which will elicit a reassurance.\n");
printf("If debug is requested by \"-D\" flag(s) on second parameter, then\n");
printf(" debug information is written into a file whose name is \n");
printf(" the rest of the second parameter (after last \"D\"); up to three\n");
printf(" \"D\" flags may be entered (but only one \"-\"):\n");
printf("Debug = 1 gives basic trace of automaton states + error-messages,\n");
printf(" = 2 logs packets as sent/received,\n");
printf(" = 3 also logs all chars as read from line, in hex.\n");
return;
} /* End of help1() */
movemem(from,to,count) /* shift block of data */
/* moves chars starting at bottom of block */
char *from, *to;
int count;
{
while (count-- > 0)
*to++ = *from++;
return;
} /* End of movemem() */
prerrpkt(msg) /* Print contents of error packet received from remote host. */
char *msg;
{
if (debug) {
fprintf(dfp,"\nKmUCL Aborting with following message from local Kermit:");
msg[50] = 0;
fprintf(dfp,"\n \"%s\"",msg);
}
return;
} /* End prerrpkt() */
/*VARARGS1*/
printmsg(fmt, a1, a2, a3, a4, a5)
/* Print message on standard output ;
* message will only be received if remote is in connect-mode. */
char *fmt;
{
printf("KmUCL: ");
printf(fmt,a1,a2,a3,a4,a5);
printf("\n\r");
return;
} /* End of printmsg() */
char rdata() /* Receive Data */
{
int num, len; /* Packet number, length */
if (debug > 2) printf(" ** rdata() ");
if (numtry++ > MAXTRY) return('A'); /* "abort" if too many tries */
switch(rpack(&len,&num,packet)) /* Get packet */
{
case 'D': /* Got Data packet */
if (num != n) /* Right packet? */
{ /* No */
if (oldtry++ > MAXTRY)
return('A'); /* If too many tries, abort */
if (num == ((n==0) ? 63:n-1)) { /* duplicate? */
spack('Y',num,0,null); /* Yes, re-ACK it */
numtry = 0; /* Reset try counter */
return(state); /* Don't write out data! */
}
else return('A'); /* sorry, wrong number */
}
/* Got data with right packet number */
if ( (num = decode(packet,len)) != 0 ) {
error("Trouble writing file, OS code %xx",num);
return('A');
}
spack('Y',n,0,null); /* Acknowledge the packet */
n = (n+1)%64; /* Bump packet number, mod 64 */
oldtry = numtry; /* Reset the try counters */
numtry = 0; /* ... */
return('D'); /* Remain in data state */
case 'F': /* Got a File Header */
if (oldtry++ > MAXTRY)
return('A'); /* If too many tries, "abort" */
if (num == ((n==0) ? 63:n-1)) /* Else check packet number */
{ /* It was the previous one */
spack('Y',num,0,null); /* ACK it again */
numtry = 0; /* Reset try counter */
return(state); /* Stay in Data state */
}
else return('A'); /* Not previous packet, "abort" */
case 'Z': /* End-Of-File */
if (num != n) return('A'); /* Must have right packet number */
spack('Y',n,0,null); /* OK, ACK it. */
fclose(fp); /* Close the file */
fp = NULL;
if (debug) {
if ( (len != 0) && (*packet == 'D') )
fprintf(dfp,"\nFile <%s> truncated by local Kermit\n",filnam);
else
fprintf(dfp,"\nFile <%s> received OK\m",filnam);
}
if ( (len != 0) && (*packet == 'D') && (image != 1) )
fprintf(fp,"\n\n*** Local Kermit Truncated this File <%s> ***\n", filnam);
n = (n+1)%64; /* Bump packet number */
return('F'); /* Go back to Receive File state */
case 'E': /* Error packet received */
prerrpkt(packet); /* Print it out and */
return('A'); /* abort */
case FALSE: /* Didn't get packet */
spack('N',n,0,null); /* Return a NAK */
return(state); /* Keep trying */
default:
error(badpack,prompt,type);
return('A'); /* Some other packet, "abort" */
}
} /* End of rdata() */
recsw() /* state table switcher for receiving files. */
{
if (debug) fprintf(dfp,"Ready to receive file\n");
if (debug > 2) printf(" ** recsw() ");
state = 'R'; /* Receive-Init is the start state */
n = 0; /* Initialize message number */
numtry = 0; /* Say no tries yet */
forever {
if (debug == 1) fprintf(dfp," recsw state: %c\n",state);
switch(state) /* Do until done */
{
case 'R': state = rinit(); break; /* Receive-Init */
case 'F': state = rfile(); break; /* Receive-File */
case 'D': state = rdata(); break; /* Receive-Data */
case 'C': return(TRUE); /* Complete state */
case 'A': return(FALSE); /* "Abort" state */
}
}
} /* End recsw() */
char rfile() /* Receive File Header */
{
int num, len; /* Packet number, length */
char filnam1[50]; /* Holds the converted file name */
if (debug > 2) printf(" ** rfile() ");
if (numtry++ > MAXTRY) return('A'); /* "abort" if too many tries */
switch(rpack(&len,&num,packet)) /* Get a packet */
{
case 'S': /* Send-Init, maybe our ACK lost */
if (oldtry++ > MAXTRY) return('A'); /* If too many tries "abort" */
if (num == ((n==0) ? 63:n-1)) { /* Previous packet, mod 64? */
len = spar(packet); /* our Send-Init parameters */
spack('Y',num,len,packet);
numtry = 0; /* Reset try counter */
return(state); /* Stay in this state */
}
else return('A'); /* Not previous packet, "abort" */
case 'Z': /* End-Of-File */
if (oldtry++ > MAXTRY) return('A');
if (num == ((n==0) ? 63:n-1)) /* Previous packet, mod 64? */
{ /* Yes, ACK it again. */
spack('Y',num,0,null);
numtry = 0;
return(state); /* Stay in this state */
}
else return('A'); /* Not previous packet, "abort" */
case 'F': /* File Header (just what we want) */
if (num != n) return('A'); /* The packet number must be right */
packet[49] = 0; /* ensure string closed */
strcpy(filnam1, packet); /* Copy the file name */
if (filnamcnv) { /* Convert upper case to lower */
for (filnam=filnam1; *filnam != '\0'; filnam++)
if (*filnam >= 'A' && *filnam <= 'Z')
*filnam |= 040;
filnam = filnam1;
}
if (debug) fprintf(dfp,"\nOpening <%s> for receiving.",filnam1);
if ((fp=fopen(filnam1,"w")) == NULL) {
error("%sCannot create <%s>",prompt,filnam1);
return('A');
}
else /* OK, give message */
if (debug) fprintf(dfp,"\nReceiving %s as %s",packet,filnam1);
spack('Y',n,0,null); /* Acknowledge the file header */
oldtry = numtry; /* Reset try counters */
numtry = 0; /* ... */
n = (n+1)%64; /* Bump packet number, mod 64 */
return('D'); /* Switch to Data state */
case 'B': /* Break transmission (EOT) */
if (num != n) return ('A'); /* Need right packet number here */
spack('Y',n,0,null); /* Say OK */
if (debug) fprintf(dfp,"All files received\n");
return('C'); /* Go to complete state */
case 'E': /* Error packet received */
prerrpkt(packet); /* Print it out and */
return('A'); /* abort */
case FALSE: /* Didn't get packet */
spack('N',n,0,null); /* Return a NAK */
return(state); /* Keep trying */
default:
error(badpack,prompt,type);
return ('A'); /* Some other packet, "abort" */
}
} /* End rfile() */
char rinit() /* Receive Initialization */
{
int len, num; /* Packet length, number */
if (debug > 2) printf(" ** rinit() ");
if (numtry++ > MAXTRY) return('A'); /* If too many tries, "abort" */
switch(rpack(&len,&num,packet)) /* Get a packet */
{
case 'S': /* Send-Init */
rpar(packet,len);
len = spar(packet);
spack('Y',n,len,packet); /* ACK with my parameters */
oldtry = numtry; /* Save old try count */
numtry = 0; /* Start a new counter */
n = (n+1)%64; /* Bump packet number, mod 64 */
return('F'); /* Enter File-Receive state */
case 'E': /* Error packet received */
prerrpkt(packet); /* Print it out and */
return('A'); /* abort */
case 'I': /* Init-parameters */
rpar(packet,len);
len = spar(packet);
spack('Y',n,len,packet); /* ack with our parameters */
n = (n+1)%64; /* Bump packet number, mod 64 */
return(state); /* & continue */
case FALSE: /* Didn't get packet */
spack('N',n,0,null); /* Return a NAK */
return(state); /* Keep trying */
default:
error(badpack,prompt,type);
return('A'); /* Some other packet type, "abort" */
}
} /* End rinit() */
rpack(len,num,data) /* Read a Packet */
int *len, *num; /* Packet length, number */
char *data; /* Packet data */
{
int i, done; /* Data character number, loop exit */
char t, /* Current input character */
cchksum, /* Our (computed) checksum */
rchksum; /* Checksum received from other host */
if (debug > 2) printf(" ** rpack() ");
timoset(timint); /* set timeout */
/* The way timeouts are handled is that the flag stays clear, indicating
that a timeout has occurred, until it is set to a value again
by the next call to timoset(). This means that the effect can
run up thro' the procedures without using longjmp(). */
t = 0;
*len = 0; /* in case times out */
while (t != SOH) { /* Wait for packet header */
t = cread() & (char)0x7f;
if (timflag == 0)
return(FALSE);
}
done = FALSE; /* Got SOH, init loop */
while (!done) /* Loop to get a packet */
{
t = cread(); /* Get character */
if (timflag == 0)
return(FALSE);
if (t == SOH) continue; /* Resynchronize if SOH */
cchksum = t; /* Start the checksum */
*len = unchar(t)-3; /* Character count */
t = cread(); /* Get character */
if (timflag == 0)
return(FALSE);
if (t == SOH) continue; /* Resynchronize if SOH */
cchksum = cchksum + t; /* Update checksum */
*num = unchar(t); /* Packet number */
t = cread(); /* Get character */
if (timflag == 0)
return(FALSE);
if (t == SOH) continue; /* Resynchronize if SOH */
cchksum = cchksum + t; /* Update checksum */
type = t; /* Packet type */
for (i=0; i<*len; i++) { /* The data itself, if any */
t = cread(); /* Get character */
if (timflag == 0)
return(FALSE); /* as tho' bad checksum */
if (t == SOH)
break; /* Resynch if SOH */
cchksum = cchksum + t; /* Update checksum */
data[i] = t; /* Put it in the data buffer */
}
if (t == SOH)
continue;
data[*len] = 0; /* Mark the end of the data */
t = cread(); /* Get last character (checksum) */
if (timflag == 0)
return(FALSE);
rchksum = unchar(t); /* Convert to numeric */
if (t == SOH) /* Resynchronize if SOH */
continue;
done = TRUE; /* Got checksum, done */
}
timocan();
if (debug) fprintf(dfp,"Packet %d received; ",*num);
if (debug>1) { /* Display incoming packet */
fprintf(dfp," type: %c;",type);
fprintf(dfp," num: %d;",*num);
fprintf(dfp," len: %d.",*len);
if (*len != 0) {
fprintf(dfp,"\n data: <");
for (i=0; i<*len; ++i)
putc(data[i],dfp);
putc('>',dfp);
} }
/* Fold in bits 7,8 to compute */
cchksum = (((cchksum&0300) >> 6)+cchksum)&077; /* final checksum */
if (cchksum != rchksum) {
if (debug)
fprintf(dfp,"\n bad checksum: rec'd %xx computed %xx; ",
rchksum, cchksum);
return(FALSE);
}
flushinput();
return(type); /* All OK, return packet type */
} /* End of rpack() */
rpar(data,len) /* instal received parameters safely */
char *data, len;
/* Set up incoming parameters to either what has been received
or, if nil, to defaults.
No return-code. */
{
char p;
/* initialize to defaults in case incoming block is short */
spsiz = 80; /* packet-length 80 chars */
timint = MYTIME; /* timeout as defined */
eol = CR; /* terminator normally CR */
quote = '#'; /* standard control-quote char */
rptflg = FALSE; /* nor repeat-quoting */
while (len-- > 0) {
p = data[len];
switch (len) { /* for each parameter */
case 0: /* MAXL */
spsiz = unchar(p);
break;
case 1: /* TIME */
timint = (unchar(p) < 5) ? 5 : unchar(p);
break;
case 2: /* NPAD */
pad = unchar(p);
break;
case 3: /* PADC */
padchar = ctl(p);
break;
case 4: /* EOL */
eol = unchar(p);
break;
case 5: /* QCTL */
quote = p;
break;
case 6: /* QBIN */
if (image == 2) {
if (p == 'Y')
qu8 = '&';
else if (isalnum(p) == 0) /* provided punctuation */
qu8 = p;
else
image = 0;
break;
}
break;
case 8: /* REPT */
if (p == '~')
rptflg = TRUE;
break;
default: /* CHKT, CAPAS etc. */
break;
} } /* end while & outer switch */
if ( (qu8 == 0) && (image == 2) ) /* invlaid setting */
image = 0;
if (debug)
fprintf(dfp,"\nParameters in: 8th-bit setting now %d, char is %c; reptflag %s.",image,qu8,logicval[-rptflg]);
return;
} /* End of rpar() */
char sbreak() /* send EoT (break) */
{
int num, len; /* Packet number, length */
if (debug > 2) printf(" ** sbreak() ");
if (numtry++ > MAXTRY) return('A'); /* If too many tries "abort" */
spack('B',n,0,packet); /* Send a B packet */
switch (rpack(&len,&num,recpkt)) /* What was the reply? */
{
case 'N': /* NAK, just stay in this state, */
num = (--num<0 ? 63:num); /* unless NAK for previous packet, */
if (n != num) /* which is just like an ACK for */
return(state); /* this packet so fall thru to... */
case 'Y': /* ACK */
if (n != num) return(state); /* If wrong ACK, fail */
numtry = 0; /* Reset try counter */
n = (n+1)%64; /* and bump packet count */
return('C'); /* Switch state to Complete */
case 'E': /* Error packet received */
prerrpkt(recpkt); /* Print it out and */
return('A'); /* abort */
case FALSE: return('C'); /* Receive failure, count as OK */
/* If timed out or etc. when completing, likely other end has gone away */
default:
error(badpack,prompt,type);
return ('A'); /* Other, "abort" */
}
} /* End sbreak() */
char sdata() /* send data packet */
{
int num, len; /* Packet number, length */
if (debug > 2) printf(" ** sdata() ");
if (numtry++ > MAXTRY) return('A'); /* If too many tries, give up */
spack('D',n,size,packet); /* Send a D packet */
switch(rpack(&len,&num,recpkt)) /* What was the reply? */
{
case 'N': /* NAK, just stay in this state, */
num = (--num<0 ? 63:num); /* unless it's NAK for next packet */
if (n != num) /* which is just like an ACK for */
return(state); /* this packet so fall thru to... */
case 'Y': /* ACK */
if (n != num) return(state); /* If wrong ACK, persist */
numtry = 0; /* Reset try counter */
n = (n+1)%64; /* Bump packet count */
if (len != 0) switch (*recpkt) { /* ACK has data */
case 'Z': /* cancel all */
case 'z':
filecount = 0; /* no more to go */
case 'X': /* cancel file */
case 'x':
return('Z');
default: /* invalid */
recpkt[20] = 0; /* truncate */
error("%sLocal Kermit sent ACK with data: <%s> - goodbye!",prompt,recpkt);
return('A');
}
if ((size = bufill(packet)) != EOF) /* data from file */
return('D'); /* Got data, stay in state D */
/* EOF can mean either really end-of-file or an error */
if ( (num = filerr()) != 0 ) { /* actual error */
error("Problem while reading file, OS code %xx",num);
return('A');
}
return('Z'); /* EOF */
case 'E': /* Error packet received */
prerrpkt(recpkt); /* Print it out and */
return('A'); /* abort */
case FALSE: return(state); /* Receive failure, stay in D */
default:
error(badpack,prompt,type);
return('A'); /* Anything else, "abort" */
}
} /* End sdata() */
sendsw()
/* Sendsw is the state table switcher for sending files. It loops until
* either it finishes, or an error is encountered. The routines called
* by sendsw are responsible for changing the state. */
{
if (debug) {
fprintf(dfp,"\nSendsw() sending file; ");
if (debug > 2)
printf(" ** sendsw() ");
}
state = 'S'; /* Send initiate is the start state */
n = 0; /* Initialize message number */
numtry = 0; /* Say no tries yet */
forever { /* Do this as long as necessary */
if (debug == 1) fprintf(dfp," sendsw state: %c\n",state);
switch(state) {
case 'S': state = sinit(); break; /* Send-Init */
case 'F': state = sfile(); break; /* Send-File */
case 'D': state = sdata(); break; /* Send-Data */
case 'Z': state = seof(); break; /* Send-End-of-File */
case 'B': state = sbreak(); break; /* Send-Break */
case 'C': return (TRUE); /* Complete */
case 'A': return (FALSE); /* "Abort" */
default: return (FALSE); /* Unknown, fail */
}
}
} /* End sendsw() */
char seof() /* Send EoF */
{
int num, len; /* Packet number, length */
if (debug > 2) printf(" ** seof() ");
if (numtry++ > MAXTRY) return('A'); /* If too many tries, "abort" */
spack('Z',n,0,packet); /* Send a 'Z' packet */
switch(rpack(&len,&num,recpkt)) /* What was the reply? */
{
case 'N': /* NAK, just stay in this state, */
num = (--num<0 ? 63:num); /* unless it's NAK for next packet, */
if (n != num) /* which is just like an ACK for */
return(state); /* this packet so fall thru to... */
case 'Y': /* ACK */
if (n != num) return(state); /* If wrong ACK, hold out */
numtry = 0; /* Reset try counter */
n = (n+1)%64; /* and bump packet count */
if (debug) fprintf(dfp,"\nClosing input file <%s>, ",filnam);
fclose(fp); /* Close the input file */
fp = NULL; /* Set flag indicating no file open */
if (debug) fprintf(dfp,"looking for next file...");
if (gnxtfl() == FALSE) { /* No more files go? */
if (debug) fprintf(dfp,"\nNo more files to send.");
return('B'); /* if not, break, EOT, all done */
}
if (debug) fprintf(dfp,"New file is %s\n",filnam);
return('F'); /* More files, switch state to F */
case 'E': /* Error packet received */
prerrpkt(recpkt); /* Print it out and */
return('A'); /* abort */
case FALSE: return(state); /* Receive failure, stay in Z */
default:
error(badpack,prompt,type);
return('A'); /* Something else, "abort" */
}
} /* End seof() */
char sfile() /* send file header */
{
int num, len; /* Packet number, length */
char filnam1[50], /* Converted file name */
*newfilnam, /* Pointer to file name to send */
*cp; /* char pointer */
if (debug > 2) printf(" ** sfile() ");
if (numtry++ > MAXTRY) return('A'); /* If too many tries, give up */
if (fp == NULL) { /* If not already open, */
if (debug) fprintf(dfp,"\nOpening %s for sending.",filnam);
fp = fopen(filnam,"r"); /* open the file to be sent */
if (fp == NULL) /* If bad file pointer, give up */
{
error("%sCannot open file <%s>",prompt,filnam);
return('A');
}
}
strcpy(filnam1, filnam); /* Copy file name */
newfilnam = cp = filnam1;
while (*cp != '\0') /* Strip off all leading directory */
if (*cp++ == '/') /* names (ie. up to the last /). */
newfilnam = cp;
if (filnamcnv) /* Convert lower case to upper */
for (cp = newfilnam; *cp != '\0'; cp++)
if (*cp >= 'a' && *cp <= 'z')
*cp ^= 040;
len = cp - newfilnam; /* Compute length of new filename */
if (debug) fprintf(dfp,"\nSending %s as %s",filnam,newfilnam);
spack('F',n,len,newfilnam); /* Send an F packet */
switch(rpack(&len,&num,recpkt)) /* What was the reply? */
{
case 'N': /* NAK, just stay in this state, */
num = (--num<0 ? 63:num); /* unless it's NAK for next packet */
case 'Y': /* ACK */
if (n != num)
return(state); /* If wrong ACK, stay in F state */
numtry = 0; /* Reset try counter */
n = (n+1)%64; /* Bump packet count */
if ((size = bufill(packet)) != EOF) /* data from file */
return('D'); /* Got data, stay in state D */
/* EOF can mean either really end-of-file or an error */
if ( (num = filerr()) != 0 ) { /* actual error */
error("Problem while reading file, OS code %xx",num);
return('A');
}
return('Z'); /* EOF (empty file) */
case 'E': /* Error packet received */
prerrpkt(recpkt); /* Print it out and */
return('A'); /* abort */
case FALSE: return(state); /* Receive failure, stay in F state */
default:
error(badpack,prompt,type);
return('A'); /* Something else, just "abort" */
}
} /* End sfile() */
char sinit() /* send initiate (exchange parameters) */
{
int num, len; /* Packet number, length */
if (debug > 2) printf(" ** sinit() ");
flushinput(); /* Flush pending input */
if (numtry++ > MAXTRY) return('A'); /* If too many tries, give up */
len = spar(packet); /* Fill up init info packet */
spack('S',n,len,packet); /* Send an S packet */
switch(rpack(&len,&num,recpkt)) /* What was the reply? */
{
case 'N':
num = (--num<0 ? 63:num); /* unless it's NAK for next packet */
if (n != num) /* which is just like an ACK for */
return(state); /* this packet so fall thru to... */
case 'Y': /* ACK */
if (n != num) /* If wrong ACK, stay in S state */
return(state); /* and try again */
rpar(recpkt,len); /* Get other side's init info */
numtry = 0; /* Reset try counter */
n = (n+1)%64; /* Bump packet count */
return('F'); /* OK, switch state to F */
case 'E': /* Error packet received */
prerrpkt(recpkt); /* Print it out and */
return('A'); /* abort */
case 'I': /* Init-parameters */
rpar(packet,len);
len = spar(packet);
spack('Y',n,len,packet); /* ack with our parameters */
n = (n+1)%64; /* Bump packet number, mod 64 */
return(state); /* & continue */
case FALSE: return(state); /* Receive failure, try again */
default:
error(badpack,prompt,type);
return('A'); /* Anything else, just "abort" */
}
} /* End sinit() */
spack(stype,num,len,data) /* Send a Packet */
char stype, *data;
int num, len;
{
int i; /* Character loop counter */
char chksum, buffer[100]; /* Checksum, packet buffer */
register char *bufp; /* Buffer pointer */
if (debug > 1) { /* Display outgoing packet */
fprintf(dfp,"\nSending packet; type: %c;",stype);
fprintf(dfp," num: %d;",num);
fprintf(dfp," len: %d;",len);
if (len != 0) {
fprintf(dfp,"\n data: <");
for (i=0; i<len; ++i)
putc(data[i],dfp);
putc('>',dfp);
} }
bufp = buffer; /* Set up buffer pointer */
for (i=1; i<=pad; i++) write(0,&padchar,1); /* Issue any padding */
*bufp++ = SOH; /* Packet marker, ASCII 1 (SOH) */
*bufp++ = tochar(len+3); /* Send the character count */
chksum = tochar(len+3); /* Initialize the checksum */
*bufp++ = tochar(num); /* Packet number */
chksum += tochar(num); /* Update checksum */
*bufp++ = stype; /* Packet type */
chksum += stype; /* Update checksum */
for (i=0; i<len; i++) /* Loop for all data characters */
{
*bufp++ = data[i]; /* Get a character */
chksum += data[i]; /* Update checksum */
}
chksum = (((chksum&0300) >> 6)+chksum)&077; /* Compute final checksum */
*bufp++ = tochar(chksum); /* Put it in the packet */
*bufp++ = eol; /* Extra-packet line terminator */
*bufp = CR; /* CR for network */
write(0, buffer,bufp-buffer+1); /* Send the packet */
if (debug) {
fprintf(dfp," Packet %d sent;\n",num);
if (debug > 2)
printf("\n\r");
}
return;
} /* End of spack() */
spar(data) /* fill up packet with own parameters */
char *data;
/* returns length of parameter block (6, 7 or 9) */
{
char len;
data[0] = tochar(spsiz);
data[1] = tochar(MYTIME);
data[2] = tochar(MYPAD);
data[3] = ctl(0);
data[4] = tochar(MYEOL);
data[5] = MYQUOTE;
len = 6;
if (image == 2) { /* 8th-bit prefixing */
if (qu8 == 0)
data[6] = 'Y';
else
data[6] = qu8; /* feed back 8-quote */
len = 7;
}
else
data[6] = 'N';
if (rptflg) { /* unless repeating turned off */
data[7] = '1'; /* 1-byte checksum */
data[8] = '~'; /* only ~ for repeating*/
len = 9;
}
if (debug)
fprintf(dfp,"\nParameters out: 8th-bit setting now %d, char is %c; reptflag %s.",image,qu8,logicval[-rptflg]);
return(len);
} /* End of spar() */
timocan() /* cancel timeout */
{
timoset(0);
timflag = 0xff;
return;
} /* End of timocan() */
timoex() /* action timeout */
{
timflag = 0; /* clear flag */
if (debug) {
printmsg("Timeout ... ");
fprintf(dfp,"Timeout ...");
}
return;
} /* End of timoex() */
usage() /* Print summary of usage info and quit */
{
cooktty();
printf("Usage: kermit s[f78i] [-Ddebug-file] file(s) ... (send mode)\n");
printf("or: kermit r[f78i] [-Ddebug-file] (receive mode)\n");
printf("or: kermit a[f78i] [-Ddebug-file] (auto mode)\n");
printf(" where f = filenames not converted to upper case,\n");
printf(" 7 = force 7-bit data transfer,\n");
printf(" 8 = attempt 8th-bit-prefixed transfer,\n");
printf(" i = force 8-bit-image transfer,\n");
printf(" and debug-file-name is preceded by \"-\" and up to 3 \"D\"-flags.\n");
printf("or: kermit h (help display).\n");
printf(crlf);
closeall();
exit(0);
} /* End of usage() */
/************** END of FILE uclk??.c **************************/