home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
kermit.columbia.edu
/
kermit.columbia.edu.tar
/
kermit.columbia.edu
/
archives
/
amdahluts.zip
/
utss1.c
next >
Wrap
C/C++ Source or Header
|
1988-08-24
|
52KB
|
1,482 lines
/*
* K e r m i t File Transfer Utility
*
* UNIX Kermit, Columbia University, 1981, 1982, 1983
* Bill Catchings, Bob Cattani, Chris Maio, Frank da Cruz, Alan Crosswell
*
* Also: Jim Guyton, Rand Corporation
* Walter Underwood, Ford Aerospace
*
* usage: kermit c [lbe line baud escapechar] to connect
* kermit s [d..iflb line baud] file ... to send files
* kermit r [d..iflb line baud] to receive files
*
* where c=connect, s=send, r=receive,
* d=debug, i=image mode, f=no filename conversion, l=tty line,
* b=baud rate, e=escape char.
*
* For remote Kermit, format is either:
* kermit r to receive files
* or kermit s file ... to send files
*
*/
/*
* Modification History:
*
* Oct. 17 Included fixes from Alan Crosswell (CUCCA) for IBM_UTS:
* - Changed MYEOL character from \n to \r.
* - Change char to int in bufill so getc would return -1 on
* EOF instead of 255 (-1 truncated to 8 bits)
* - Added read() in rpack to eat the EOL character
* - Added fflush() call in printmsg to force the output
* NOTE: The last three changes are not conditionally compiled
* since they should work equally well on any system.
*
* Changed Berkeley 4.x conditional compilation flag from
* UNIX4X to UCB4X.
* Added support for error packets and cleaned up the printing
* routines.
*/
#include <stdio.h> /* Standard UNIX definitions */
/*Conditional compilation for different machines/operating systems */
/*One and only one of the following lines should be 1 */
#define UCB4X 0 /* Berkeley 4.x UNIX */
#define TOPS_20 0 /* TOPS-20 */
#define IBM_UTS 1 /* Amdahl UTS on IBM systems */
#define VAX_VMS 0 /* VAX/VMS (not yet implemented) */
/*Conditional compilation for the different Unix variants */
/*0 means don't compile it, nonzero means do */
#if UCB4X
#define V6_LIBS 0 /* Dont't use retrofit libraries */
#define NO_FIONREAD 0 /* We have ioctl(FIONREAD,...) for flushinput() */
#define NO_TANDEM 0 /* We have TANDEM line discipline (xon/xoff) */
#endif
#if IBM_UTS
#define V6_LIBS 0 /* Don't use retrofit libraries */
#define NO_FIONREAD 1 /* No ioctl(FIONREAD,...) for flushinput() */
#define NO_TANDEM 1 /* No TANDEM line discipline (xon/xoff) */
#endif
#if V6_LIBS
#include <retrofit/sgtty.h>
#include <retrofit/signal.h>
#include <retrofit/setjmp.h>
#else
#include <sgtty.h>
#include <signal.h>
#include <setjmp.h>
#endif
#if NO_TANDEM
#define TANDEM 0 /* define it to be nothing if it's unsupported */
#endif
/*Symbol Definitions */
#define MAXPACKSIZ 94 /* Maximum packet size */
#define SOH 1 /* Start of header */
#define CR 13 /* ASCII Carriage Return */
#define SP 32 /* ASCII space */
#define DEL 127 /* Delete (rubout) */
#define ESCCHR '^' /* Default escape character for CONNECT */
#define MAXTRY 10 /* Times to retry a packet */
#define MYQUOTE '#' /* Quote character I will use */
#define MYPAD 0 /* Number of padding characters I will need */
#define MYPCHAR 0 /* Padding character I need (NULL) */
#if IBM_UTS
#define MYEOL '\r' /* End-Of-Line character for UTS systems */
#else
#define MYEOL '\n' /* End-Of-Line character I need */
#endif
#define MYTIME 10 /* Seconds after which I should be timed out */
#define MAXTIM 60 /* Maximum timeout interval */
#define MINTIM 2 /* Minumum timeout interval */
#define TRUE -1 /* Boolean constants */
#define FALSE 0
/*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 )
/*Global Variables */
int size, /* Size of present data */
rpsiz, /* Maximum receive packet size */
spsiz, /* Maximum send packet size */
pad, /* How much padding to send */
timint, /* Timeout for foreign host on sends */
n, /* Packet number */
numtry, /* Times this packet retried */
oldtry, /* Times previous packet retried */
ttyfd, /* File descriptor of tty for I/O, 0 if remote */
remote, /* -1 means we're a remote kermit */
image, /* -1 means 8-bit mode */
debug, /* indicates level of debugging output (0=none) */
filnamcnv, /* -1 means do file name case conversions */
filecount; /* Number of files left to send */
char state, /* Present state of the automaton */
padchar, /* Padding character to send */
eol, /* End-Of-Line character to send */
escchr, /* Connect command escape character */
quote, /* Quote character in incoming data */
**filelist, /* List of files to be sent */
*filnam, /* Current file name */
recpkt[MAXPACKSIZ], /* Receive packet buffer */
packet[MAXPACKSIZ]; /* Packet buffer */
FILE *fp, /* File pointer for current disk file */
*log; /* File pointer for Logfile */
jmp_buf env; /* Environment ptr for timeout longjump */
#ifdef UTSS1
int rpos;
char fullbuff[1920];
#endif
/*
* 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 */
{
#ifndef UTSS1
char *ttyname, /* tty name for LINE argument */
*cp; /* char pointer */
int speed, /* speed of assigned tty, */
cflg, rflg, sflg; /* flags for CONNECT, RECEIVE, SEND */
#else
char *cp;
int rflg,sflg;
char *name;
char *ttyname();
#endif
struct sgttyb
rawmode, /* Controlling tty raw mode */
cookedmode, /* Controlling tty cooked mode */
ttymode; /* mode of tty line in LINE option */
if (argc < 2) usage(); /* Make sure there's a command line */
cp = *++argv; argv++; argc -= 2; /* Set up 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 */
#ifndef UTSS1
speed = cflg = sflg = rflg = 0; /* Turn off all parse flags */
ttyname = 0; /* Default is remote mode */
#endif
#if UCB4X /* Default to 7-bit masking, CRLF */
image = FALSE; /* translation and filename case */
filnamcnv = TRUE; /* conversion for UNIX systems */
#else
image = TRUE; /* Default to no processing for */
filnamcnv = FALSE; /* non-UNIX systems */
#endif
#ifdef UTSS1
image = FALSE;
filnamcnv = TRUE;
#endif
escchr = ESCCHR; /* Default escape character */
#ifdef UTSS1
sflg = rflg = 0;
#endif
while ((*cp) != NULL) /* Parse characters in first arg. */
switch (*cp++)
{
#ifndef UTSS1
case 'c': cflg++; break; /* C = Connect command */
#endif
case 's': sflg++; break; /* S = Send command */
case 'r': rflg++; break; /* R = Receive command */
#ifndef UTSS1
case 'd': /* D = Increment debug mode count */
debug++; break;
#endif
case 'f':
filnamcnv = FALSE; /* F = don't do case conversion */
break; /* on filenames */
#ifndef UTSS1
case 'i': /* I = Image (8-bit) mode */
image = TRUE; break; /* (this is default for non-UNIX) */
case 'l': /* L = specify tty line to use */
if (argc--) ttyname = *argv++;
else usage();
if (debug) printf("Line to remote host is %s\n",ttyname);
break;
case 'e': /* E = specify escape char */
if (argc--) escchr = **argv++;
else usage();
if (debug) printf("Escape char is \"c\"\n",escchr);
break;
case 'b': /* B = specify baud rate */
#endif
#if UCB4X
if (argc--) speed = atoi(*argv++);
else usage();
if (debug) printf("Line speed to remote host is %d\n",speed);
break;
#else
printmsg("Speed setting implemented for Unix only.");
exit(1);
#endif
}
/*Done parsing */
#ifndef UTSS1
if ((cflg+sflg+rflg) != 1) /* Only one command allowed */
usage();
if (ttyname) /* If LINE was specified, we */
{ /* operate in local mode */
ttyfd = open(ttyname,2); /* Open the tty line */
if (ttyfd < 0)
{
printmsg("Cannot open %s",ttyname);
exit(1);
}
remote = FALSE; /* Indicate we're in local mode */
}
else /* No LINE specified so we operate */
{ /* in remote mode (ie. controlling */
ttyfd = 0; /* tty is the communications line) */
remote = TRUE;
}
/*Put the proper tty into the correct mode */
if (remote) /* If remote, use controlling tty */
{
gtty(0,&cookedmode); /* Save current mode so we can */
gtty(0,&rawmode); /* restore it later */
rawmode.sg_flags |= (RAW|TANDEM);
rawmode.sg_flags &= ~(ECHO|CRMOD);
stty(0,&rawmode); /* Put tty in raw mode */
}
else /* Local, use assigned line */
{
gtty(ttyfd,&ttymode);
ttymode.sg_flags |= (RAW|TANDEM);
ttymode.sg_flags &= ~(ECHO|CRMOD);
#endif
#if UCB4X /* Speed changing for UNIX only */
if (speed) /* User specified a speed? */
{
switch(speed) /* Get internal system code */
{
case 110: speed = B110; break;
case 150: speed = B150; break;
case 300: speed = B300; break;
case 1200: speed = B1200; break;
case 2400: speed = B2400; break;
case 4800: speed = B4800; break;
case 9600: speed = B9600; break;
default:
printmsg("Bad line speed.");
exit(1);
}
ttymode.sg_ispeed = speed;
ttymode.sg_ospeed = speed;
}
#endif /* UCB4X */
#ifndef UTSS1
stty(ttyfd,&ttymode); /* Put asg'd tty in raw mode */
}
/*All set up, now execute the command that was given. */
if (debug)
{
printf("Debugging level = %d\n\n",debug);
if (cflg) printf("Connect command\n\n");
if (sflg) printf("Send command\n\n");
if (rflg) printf("Receive command\n\n");
}
if (cflg) connect(); /* Connect command */
#else
if ((rflg + sflg) != 1) usage();
name = ttyname(2);
name[6] = 'u';
name[7] = 'b';
ttyfd = open(name,2);
ioctl(ttyfd,TUBOCMD,0x01);
remote = FALSE;
#endif
if (sflg) /* Send command */
{
if (argc--) filnam = *argv++; /* Get file to send */
else
{
#ifndef UTSS1
if (remote)
stty(0,&cookedmode); /* Restore controlling tty's modes */
#endif
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 */
if (sendsw() == FALSE) /* Send the file(s) */
printmsg("Send failed."); /* Report failure */
else /* or */
printmsg("done."); /* success */
}
if (rflg) /* Receive command */
{
if (recsw() == FALSE) /* Receive the file(s) */
printmsg("Receive failed.");
else /* Report failure */
printmsg("done."); /* or success */
}
#ifndef UTSS1
if (remote) stty(0,&cookedmode); /* Restore controlling tty's modes */
#else
close(ttyfd);
#endif
}
/*
* s e n d s w
*
* 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.
*
*/
sendsw()
{
char sinit(), sfile(), sdata(), seof(), sbreak();
state = 'S'; /* Send initiate is the start state */
n = 0; /* Initialize message number */
numtry = 0; /* Say no tries yet */
while(TRUE) /* Do this as long as necessary */
{
if (debug) printf("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 */
}
}
}
/*
* s i n i t
*
* Send Initiate: send this host's parameters and get other side's back.
*/
char sinit()
{
int num, len; /* Packet number, length */
if (numtry++ > MAXTRY) return('A'); /* If too many tries, give up */
spar(packet); /* Fill up init info packet */
#ifndef UTSS1
flushinput(); /* Flush pending input */
#endif
spack('S',n,6,packet); /* Send an S packet */
switch(rpack(&len,&num,recpkt)) /* What was the reply? */
{
case 'N': return(state); /* NAK, try it again */
case 'Y': /* ACK */
if (n != num) /* If wrong ACK, stay in S state */
return(state); /* and try again */
rpar(recpkt); /* Get other side's init info */
if (eol == 0) eol = '\n'; /* Check and set defaults */
if (quote == 0) quote = '#';
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 FALSE: return(state); /* Receive failure, try again */
default: return('A'); /* Anything else, just "abort" */
}
}
/*
* s f i l e
*
* Send File Header.
*/
char sfile()
{
int num, len; /* Packet number, length */
char filnam1[50], /* Converted file name */
*newfilnam, /* Pointer to file name to send */
*cp; /* char pointer */
if (numtry++ > MAXTRY) return('A'); /* If too many tries, give up */
if (fp == NULL) /* If not already open, */
{ if (debug) printf(" Opening %s for sending.\n",filnam);
fp = fopen(filnam,"r"); /* open the file to be sent */
if (fp == NULL) /* If bad file pointer, give up */
{
error("Cannot open file %s",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 */
printmsg("Sending %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 */
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, stay in F state */
numtry = 0; /* Reset try counter */
n = (n+1)%64; /* Bump packet count */
size = bufill(packet); /* Get first data from file */
return('D'); /* Switch state to D */
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: return('A'); /* Something else, just "abort" */
}
}
/*
* s d a t a
*
* Send File Data
*/
char sdata()
{
int num, len; /* Packet number, length */
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, fail */
numtry = 0; /* Reset try counter */
n = (n+1)%64; /* Bump packet count */
if ((size = bufill(packet)) == EOF) /* Get data from file */
return('Z'); /* If EOF set state to that */
return('D'); /* Got data, stay in state D */
case 'E': /* Error packet received */
prerrpkt(recpkt); /* Print it out and */
return('A'); /* abort */
case FALSE: return(state); /* Receive failure, stay in D */
default: return('A'); /* Anything else, "abort" */
}
}
/*
* s e o f
*
* Send End-Of-File.
*/
char seof()
{
int num, len; /* Packet number, length */
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) printf(" Closing input file %s, ",filnam);
fclose(fp); /* Close the input file */
fp = NULL; /* Set flag indicating no file open */
if (debug) printf("looking for next file...\n");
if (gnxtfl() == FALSE) /* No more files go? */
return('B'); /* if not, break, EOT, all done */
if (debug) printf(" 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: return('A'); /* Something else, "abort" */
}
}
/*
* s b r e a k
*
* Send Break (EOT)
*/
char sbreak()
{
int num, len; /* Packet number, length */
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(state); /* Receive failure, stay in B */
default: return ('A'); /* Other, "abort" */
}
}
/*
* r e c s w
*
* This is the state table switcher for receiving files.
*/
recsw()
{
char rinit(), rfile(), rdata(); /* Use these procedures */
state = 'R'; /* Receive-Init is the start state */
n = 0; /* Initialize message number */
numtry = 0; /* Say no tries yet */
while(TRUE)
{
#ifndef UTSS1
if (debug) printf(" recsw state: %c\n",state);
#else
#endif
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 */
}
}
}
/*
* r i n i t
*
* Receive Initialization
*/
char rinit()
{
int len, num; /* Packet length, number */
#ifdef UTSS1
write(ttyfd,"\360\021\135\177\021\000\001",7);
#endif
if (numtry++ > MAXTRY) return('A'); /* If too many tries, "abort" */
switch(rpack(&len,&num,packet)) /* Get a packet */
{
case 'S': /* Send-Init */
rpar(packet); /* Get the other side's init data */
spar(packet); /* Fill up packet with my init info */
spack('Y',n,6,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(recpkt); /* Print it out and */
return('A'); /* abort */
case FALSE: /* Didn't get packet */
spack('N',n,0,0); /* Return a NAK */
return(state); /* Keep trying */
default: return('A'); /* Some other packet type, "abort" */
}
}
/*
* r f i l e
*
* Receive File Header
*/
char rfile()
{
int num, len; /* Packet number, length */
char filnam1[50]; /* Holds the converted file name */
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? */
{ /* Yes, ACK it again with */
spar(packet); /* our Send-Init parameters */
spack('Y',num,6,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,0);
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 */
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;
if ((fp=fopen(filnam1,"w"))==NULL) /* Try to open a new file */
{
error("Cannot create %s",filnam1); /* Give up if can't */
return('A');
}
else /* OK, give message */
printmsg("Receiving %s as %s",packet,filnam1);
spack('Y',n,0,0); /* 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 */
#ifndef UTSS1
spack('Y',n,0,0); /* Say OK */
#else
spacks1e('Y',n,0,0); /* Say OK with no more reads */
#endif
return('C'); /* Go to complete state */
case 'E': /* Error packet received */
prerrpkt(recpkt); /* Print it out and */
return('A'); /* abort */
case FALSE: /* Didn't get packet */
spack('N',n,0,0); /* Return a NAK */
return(state); /* Keep trying */
default: return ('A'); /* Some other packet, "abort" */
}
}
/*
* r d a t a
*
* Receive Data
*/
char rdata()
{
int num, len; /* Packet number, length */
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)) /* Else check packet number */
{ /* Previous packet again? */
spack('Y',num,6,packet); /* 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 */
bufemp(packet,len); /* Write the data to the file */
spack('Y',n,0,0); /* Acknowledge the packet */
oldtry = numtry; /* Reset the try counters */
numtry = 0; /* ... */
n = (n+1)%64; /* Bump packet number, mod 64 */
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,0); /* 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,0); /* OK, ACK it. */
fclose(fp); /* Close the file */
n = (n+1)%64; /* Bump packet number */
return('F'); /* Go back to Receive File state */
case 'E': /* Error packet received */
prerrpkt(recpkt); /* Print it out and */
return('A'); /* abort */
case FALSE: /* Didn't get packet */
spack('N',n,0,0); /* Return a NAK */
return(state); /* Keep trying */
default: return('A'); /* Some other packet, "abort" */
}
}
#ifndef UTSS1
/*
* c o n n e c t
*
* Establish a virtual terminal connection with the remote host, over an
* assigned tty line.
*/
connect()
{
int pid, /* Holds process id of child */
connected; /* Boolean connect flag */
char bel = '\07',
c;
struct sgttyb
rawmode, /* Controlling tty raw mode */
cookedmode; /* Controlling tty cooked mode */
if (remote) /* Nothing to connect to in remote */
{ /* mode, so just return */
printmsg("No line specified for connection.");
return;
}
gtty(0,&cookedmode); /* Save current mode so we can */
gtty(0,&rawmode); /* restore it later */
rawmode.sg_flags |= (RAW|TANDEM);
rawmode.sg_flags &= ~(ECHO|CRMOD);
stty(0,&rawmode); /* Put tty in raw mode */
pid = fork(); /* Start fork to get typeout from remote host */
if (pid) /* Parent: send type-in to remote host */
{
printmsg("connected...\r");
connected = TRUE; /* Put us in "connect mode" */
while (connected)
{
read(0,&c,1); /* Get a character */
if ((c&0177) == escchr) /* Check for escape character */
{
read(0,&c,1);
if ((c&0177) == escchr)
write(ttyfd,&c,1);
else
switch (c&0177)
{
case 'c':
case 'C':
connected = FALSE;
write(0,"\r\n",2);
break;
case 'h':
case 'H':
write(0,"\r\nYes, I'm still here...\r\n",26);
break;
default:
write(0,&bel,1);
break;
}
}
else
{ /* If not escape charater, */
write(ttyfd,&c,1); /* write it out */
c = NULL; /* Nullify it (why?) */
}
}
kill(pid,9); /* Done, kill the child */
wait(0); /* and bury him */
stty(0,&cookedmode); /* Restore tty mode */
printmsg("disconnected.");
return; /* Done */
}
else /* Child does the reading from the remote host */
{
while(1) /* Do this forever */
{
read(ttyfd,&c,1);
write(1,&c,1);
}
}
}
#endif
%
* KERMIT utilities.
*/
clkint() /* Timer interrupt handler */
{
longjmp(env,TRUE); /* Tell rpack to give up */
}
/*
* s p a c k
*
* Send a Packet
*/
spack(type,num,len,data)
char type, *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 */
{
if (data != NULL)
data[len] = '\0'; /* Null-terminate data to print it */
printf(" spack type: %c\n",type);
printf(" num: %d\n",num);
printf(" len: %d\n",len);
if (data != NULL)
printf(" data: \"%s\"\n",data);
}
bufp = buffer; /* Set up buffer pointer */
#ifdef UTSS1
*bufp++ = '\360';
*bufp++ = '\021';
*bufp++ = '\135';
*bufp++ = '\177';
*bufp++ = '\021';
*bufp++ = '\000';
*bufp++ = '\001';
#endif
for (i=1; i<=pad; i++) write(ttyfd,&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++ = type; /* Packet type */
chksum += type; /* 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 */
write(ttyfd, buffer,bufp-buffer+1); /* Send the packet */
}
#ifdef UTSS1
/*
* s p a c k s 1 e
*
* Send a Packet
*/
spacks1e(type,num,len,data)
char type, *data;
int num, len;
{
int i; /* Character loop counter */
char chksum, buffer[100]; /* Checksum, packet buffer */
register char *bufp; /* Buffer pointer */
bufp = buffer; /* Set up buffer pointer */
*bufp++ = '\360';
*bufp++ = '\021';
*bufp++ = '\135';
*bufp++ = '\177';
*bufp++ = '\021';
*bufp++ = '\000';
*bufp++ = '\000';
for (i=1; i<=pad; i++) write(ttyfd,&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++ = type; /* Packet type */
chksum += type; /* 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 */
write(ttyfd, buffer,bufp-buffer+1); /* Send the packet */
}
/*
* c r e a d
*/
cread(fd,buf,nbytes)
int fd;
char *buf;
int nbytes;
{
int i;
i = fd; /* this is to stop the compiler from complaining */
for (i=0;i<nbytes;i++)
buf[i] = fullbuff[i+rpos] & 0177;
rpos += nbytes;
return(nbytes);
}
#endif
/*
* r p a c k
*
* Read a Packet
*/
rpack(len,num,data)
int *len, *num; /* Packet length, number */
char *data; /* Packet data */
{
int i, done; /* Data character number, loop exit */
char t, /* Current input character */
type, /* Packet type */
cchksum, /* Our (computed) checksum */
rchksum; /* Checksum received from other host */
#if UCB4X /* TOPS-20 can't handle timeouts... */
if (setjmp(env)) return FALSE; /* Timed out, fail */
signal(SIGALRM,clkint); /* Setup the timeout */
if ((timint > MAXTIM) || (timint < MINTIM)) timint = MYTIME;
alarm(timint);
#endif /* UCB4X */
#ifdef UTSS1
for(i=0;i<1920;i++) fullbuff[i] = 0;
read(ttyfd,fullbuff,1920);
rpos = 3;
#endif
while (t != SOH) /* Wait for packet header */
{
#ifndef UTSS1
read(ttyfd,&t,1);
#else
cread(ttyfd,&t,1);
#endif
t &= 0177; /* Handle parity */
}
done = FALSE; /* Got SOH, init loop */
while (!done) /* Loop to get a packet */
{
#ifndef UTSS1
read(ttyfd,&t,1); /* Get character */
#else
cread(ttyfd,&t,1); /* Get character */
#endif
if (!image) t &= 0177; /* Handle parity */
if (t == SOH) continue; /* Resynchronize if SOH */
cchksum = t; /* Start the checksum */
*len = unchar(t)-3; /* Character count */
#ifndef UTSS1
read(ttyfd,&t,1); /* Get character */
#else
cread(ttyfd,&t,1); /* Get character */
#endif
if (!image) t &= 0177; /* Handle parity */
if (t == SOH) continue; /* Resynchronize if SOH */
cchksum = cchksum + t; /* Update checksum */
*num = unchar(t); /* Packet number */
#ifndef UTSS1
read(ttyfd,&t,1); /* Get character */
#else
cread(ttyfd,&t,1); /* Get character */
#endif
if (!image) t &= 0177; /* Handle parity */
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 */
{ /* Loop for character count */
#ifndef UTSS1
read(ttyfd,&t,1); /* Get character */
#else
cread(ttyfd,&t,1); /* Get character */
#endif
if (!image) t &= 0177; /* Handle parity */
if (t == SOH) continue; /* Resynch if SOH */
cchksum = cchksum + t; /* Update checksum */
data[i] = t; /* Put it in the data buffer */
}
data[*len] = 0; /* Mark the end of the data */
#ifndef UTSS1
read(ttyfd,&t,1); /* Get last character (checksum) */
#else
cread(ttyfd,&t,1); /* Get last character (checksum) */
#endif
rchksum = unchar(t); /* Convert to numeric */
#ifndef UTSS1
read(ttyfd,&t,1); /* get EOL character and toss it */
#else
cread(ttyfd,&t,1); /* get EOL character and toss it */
#endif
if (!image) t &= 0177; /* Handle parity */
if (t == SOH) continue; /* Resynchronize if SOH */
done = TRUE; /* Got checksum, done */
}
#if UCB4X
alarm(0); /* Disable the timer interrupt */
#endif
if (debug>1) /* Display incoming packet */
{
if (data != NULL)
data[*len] = '\0'; /* Null-terminate data to print it */
printf(" rpack type: %c\n",type);
printf(" num: %d\n",*num);
printf(" len: %d\n",*len);
if (data != NULL)
printf(" data: \"%s\"\n",data);
}
/* Fold in bits 7,8 to compute */
cchksum = (((cchksum&0300) >> 6)+cchksum)&077; /* final checksum */
if (cchksum != rchksum) return(FALSE);
return(type); /* All OK, return packet type */
}
/*
* b u f i l l
*
* Get a bufferful of data from the file that's being sent.
* Only control-quoting is done; 8-bit & repeat count prefixes are
* not handled.
*/
bufill(buffer)
char buffer[]; /* Buffer */
{
int i, /* Loop index */
t; /* Char read from file */
char t7; /* 7-bit version of above */
i = 0; /* Init data buffer pointer */
while((t = getc(fp)) != EOF) /* Get the next character */
{
t7 = t & 0177; /* Get low order 7 bits */
if (t7 < SP || t7==DEL || t7==quote) /* Does this char require */
{ /* special handling? */
if (t=='\n' && !image)
{ /* Do LF->CRLF mapping if !image */
buffer[i++] = quote;
buffer[i++] = ctl('\r');
}
buffer[i++] = quote; /* Quote the character */
if (t7 != quote)
{
t = ctl(t); /* and uncontrolify */
t7 = ctl(t7);
}
}
if (image)
buffer[i++] = t; /* Deposit the character itself */
else
buffer[i++] = t7;
if (i >= spsiz-8) return(i); /* Check length */
}
if (i==0) return(EOF); /* Wind up here only on EOF */
return(i); /* Handle partial buffer */
}
/*
* b u f e m p
*
* Put data from an incoming packet into a file.
*/
bufemp(buffer,len)
char buffer[]; /* Buffer */
int len; /* Length */
{
int i; /* Counter */
char t; /* Character holder */
for (i=0; i<len; i++) /* Loop thru the data field */
{
t = buffer[i]; /* Get character */
if (t == MYQUOTE) /* Control quote? */
{ /* Yes */
t = buffer[++i]; /* Get the quoted character */
if ((t & 0177) != MYQUOTE) /* Low order bits match quote char? */
t = ctl(t); /* No, uncontrollify it */
}
if (t==CR && !image) /* Don't pass CR if in image mode */
continue;
putc(t,fp);
}
}
/*
* g n x t f l
*
* Get next file in a file group
*/
gnxtfl()
{
if (debug) printf(" gnxtfl: filelist = \"%s\"\n",*filelist);
filnam = *(filelist++);
if (filecount-- == 0) return FALSE; /* If no more, fail */
else return TRUE; /* else succeed */
}
/*
* s p a r
*
* Fill the data array with my send-init parameters
*
*/
spar(data)
char data[];
{
data[0] = tochar(MAXPACKSIZ); /* Biggest packet I can receive */
data[1] = tochar(MYTIME); /* When I want to be timed out */
data[2] = tochar(MYPAD); /* How much padding I need */
data[3] = ctl(MYPCHAR); /* Padding character I want */
data[4] = tochar(MYEOL); /* End-Of-Line character I want */
data[5] = MYQUOTE; /* Control-Quote character I send */
}
/* r p a r
*
* Get the other host's send-init parameters
*
*/
rpar(data)
char data[];
{
spsiz = unchar(data[0]); /* Maximum send packet size */
timint = unchar(data[1]); /* When I should time out */
pad = unchar(data[2]); /* Number of pads to send */
padchar = ctl(data[3]); /* Padding character to send */
eol = unchar(data[4]); /* EOL character I must send */
quote = data[5]; /* Incoming data quote character */
}
/*
* f l u s h i n p u t
*
* Dump all pending input to clear stacked up NACK's.
* (Implemented only for Berkeley Unix at this time).
*/
#if UCB4X&(~NO_FIONREAD)
flushinput()
{
long int count; /* Number of bytes ready to read */
long int i; /* Number of bytes to read in loop */
ioctl(ttyfd, FIONREAD, &count); /* See how many bytes pending read */
if (!count) return; /* If zero, then no input to flush */
while (count) /* Loop till all are flushed */
{
i = (count<sizeof(recpkt)) ? /* Read min of count and size of */
count : sizeof(recpkt); /* the read buffer */
read(ttyfd, recpkt, i); /* Read a bunch */
count -= i; /* Subtract from amount to read */
}
}
#else
flushinput() /* Null version for non-Berkeley Unix */
{}
#endif /* UCB4X&(~FIONREAD) */
/*
* Kermit printing routines:
*
* usage - print command line options showing proper syntax
* printmsg - like printf with "Kermit: " prepended
* error - like printmsg if local kermit; sends a error packet if remote
* prerrpkt - print contents of error packet received from remote host
*/
/*
* u s a g e
*
* Print summary of usage info and quit
*/
usage()
{
#if UCB4X
printf("Usage: kermit c[lbe line baud esc.char] (connect mode)\n");
printf("or: kermit s[diflb line baud] file ... (send mode)\n");
printf("or: kermit r[diflb line baud] (receive mode)\n");
#else
printf("Usage: kermit c[le line esc.char] (connect mode)\n");
printf("or: kermit s[difl line] file ... (send mode)\n");
printf("or: kermit r[difl line] (receive mode)\n");
#endif
exit(1);
}
/*
* p r i n t m s g
*
* Print message on standard output if not remote.
*/
/*VARARGS1*/
#ifndef UTSS1
printmsg(fmt, a1, a2, a3, a4, a5)
char *fmt;
{
if (!remote)
{
printf("Kermit: ");
printf(fmt,a1,a2,a3,a4,a5);
printf("\n");
fflush(stdout); /* force output (UTS needs it) */
}
}
#else
printmsg(fmt, a1, a2, a3, a4, a5)
char *fmt;
{
}
#endif
/*
* e r r o r
*
* Print error message.
*
* If local, print error message with printmsg.
* If remote, send an error packet with the message.
*/
/*VARARGS1*/
#ifndef UTSS1
error(fmt, a1, a2, a3, a4, a5)
char *fmt;
{
char msg[80];
int len;
if (remote)
{
sprintf(msg,fmt,a1,a2,a3,a4,a5); /* Make it a string */
len = strlen(msg);
spack('E',n,len,msg); /* Send the error packet */
}
else
printmsg(fmt, a1, a2, a3, a4, a5);
return;
}
#else
error(fmt, a1, a2, a3, a4, a5)
char *fmt;
{
}
#endif
/*
* p r e r r p k t
*
* Print contents of error packet received from remote host.
*/
#ifndef UTSS1
prerrpkt(msg)
char *msg;
{
printf("Kermit aborting with following error from remote host:\n%s\n",msg);
return;
}
#else
prerrpkt(msg)
char *msg;
{
}
#endif