[net.sources] Kermit Source

trio@idis.UUCP (02/25/85)

Due to the overwhelming number of requests for kermit for the UNIX (yeah, 
UNIX is a trademark of Bell Labs), I decided to post the source in 
net.sources.  If you have any questions, e-mail to me (I love e-mail, it's
lonely being a sociology grad student!). I shall also post all relevant docs.

Nicholas R. Trio     University of Pittsburgh    ...decvax!mcnc!idis!trio

------------------------------CUT HERE-----------------------------------
/* 
 *  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	    1	    /* Berkeley 4.x UNIX */ 
#define TOPS_20	    0	    /* TOPS-20 */ 
#define IBM_UTS	    0	    /* 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 */ 
 
 
/* 
 *  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 *ttyname,			/* tty name for LINE argument */ 
	*cp;				/* char pointer */ 
    int speed,				/* speed of assigned tty, */ 
	cflg, rflg, sflg;		/* flags for CONNECT, RECEIVE, SEND */ 
 
    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 */ 
 
    speed = cflg = sflg = rflg = 0;	/* Turn off all parse flags */ 
    ttyname = 0;			/* Default is remote mode */ 
 
#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 
 
    escchr = ESCCHR;			/* Default escape character */ 
  
    while ((*cp) != NULL)		/* Parse characters in first arg. */ 
	switch (*cp++) 
	{ 
	    case 'c': cflg++; break;	/* C = Connect command */ 
	    case 's': sflg++; break;	/* S = Send command */ 
	    case 'r': rflg++; break;	/* R = Receive command */ 
 
	    case 'd':			/* D = Increment debug mode count */ 
		debug++; break; 
		 
	    case 'f': 
		filnamcnv = FALSE;	/* F = don't do case conversion */ 
		break;			/*     on filenames */ 
 
	    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 */ 
#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 */ 
 
    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); 
 
#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 */ 
 
	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 */ 
 
    if (sflg)				/* Send command */  
    { 
	if (argc--) filnam = *argv++;	/* Get file to send */ 
	else 
	{   if (remote) 
		stty(0,&cookedmode);	/* Restore controlling tty's modes */ 
	    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 */ 
    } 
 
    if (remote) stty(0,&cookedmode);	/* Restore controlling tty's modes */ 
} 
 
 
/* 
 *  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 */ 
 
    flushinput();			/* Flush pending input */ 
 
    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) 
    { 
	if (debug) printf(" 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 */ 
	} 
    } 
} 
 
     
/* 
 *  r i n i t 
 * 
 *  Receive Initialization 
 */ 
   
char rinit() 
{ 
    int len, num;			/* Packet length, number */ 
 
    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 */ 
	    spack('Y',n,0,0);		/* Say OK */ 
	    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" */ 
    } 
} 
 
/* 
 *  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); 
	} 
    } 
} 
 
/* 
 *	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 */ 
    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 */ 
} 
 
/* 
 *  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 */ 
 
    while (t != SOH)			/* Wait for packet header */ 
    { 
	read(ttyfd,&t,1); 
	t &= 0177;			/* Handle parity */ 
    } 
 
    done = FALSE;			/* Got SOH, init loop */ 
    while (!done)			/* Loop to get a packet */ 
    { 
	read(ttyfd,&t,1);		/* Get character */ 
	if (!image) t &= 0177;		/* Handle parity */ 
	if (t == SOH) continue;		/* Resynchronize if SOH */ 
	cchksum = t;			/* Start the checksum */ 
	*len = unchar(t)-3;		/* Character count */ 
 
	read(ttyfd,&t,1);		/* Get character */ 
	if (!image) t &= 0177;		/* Handle parity */ 
	if (t == SOH) continue;		/* Resynchronize if SOH */ 
	cchksum = cchksum + t;		/* Update checksum */ 
	*num = unchar(t);		/* Packet number */ 
 
	read(ttyfd,&t,1);		/* Get character */ 
	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 */ 
	    read(ttyfd,&t,1);		/* Get character */ 
	    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 */ 
 
	read(ttyfd,&t,1);		/* Get last character (checksum) */ 
	rchksum = unchar(t);		/* Convert to numeric */ 
	read(ttyfd,&t,1);		/* get EOL character and toss it */ 
	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*/ 
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) */ 
    } 
} 
 
/* 
 *  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*/ 
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; 
} 
 
/* 
 *  p r e r r p k t 
 * 
 *  Print contents of error packet received from remote host. 
 */ 
prerrpkt(msg) 
char *msg; 
{ 
    printf("Kermit aborting with following error from remote host:\n%s\n",msg);
    return;
}

rcb@rti-sel.UUCP (Random) (02/26/85)

This version of kermit is an absolute piece of trash. There is a full
server implementation of kermit available from columbia university.

					Random
					Research Triangle Institute
					...!mcnc!rti-sel!rcb

avolio@grendel.UUCP (Frederick M. Avolio) (02/27/85)

> This version of kermit is an absolute piece of trash. There is a full
> server implementation of kermit available from columbia university.

Nice, huh?  Here's a person who sat  and  thought  out  his/her  words
carefully  and  decided  that  "absolute  piece  of trash" was a nice,
clear, helpful way of commenting on code that a person  or  people  1)
perhaps  worked  very  hard  on and 2) decided to give away.  It is so
bloody easy to rip up someone else's work, isn't it? (And, no, I don't
have anything to do with the poster of the source.)
-- 
Fred Avolio      {decvax,seismo}!grendel!avolio      301/731-4100 x4227

tsc2597@acf4.UUCP (Sam Chin) (02/27/85)

<<<>>>

Note that for BSD4.2, this is an obsolete version of KERMIT. An advanced
version with server mode etc. is available from CU20B via anonymous ftp.
The prefix is CX* (I think. If not just read 00readme.txt)

                                       Sam Chin
                                       allegra!cmcl2!acf4!tsc2597
                                       tsc2597@nyu-acf4

mike@dolqci.UUCP (Mike Stalnaker) (02/27/85)

> This version of kermit is an absolute piece of trash. There is a full
> server implementation of kermit available from columbia university.
> 
> 					Random
> 					Research Triangle Institute
> 					...!mcnc!rti-sel!rcb

Several Comments.... 1) If the version from columbia university is so
nice (please note: I am not saying anything against columbia's version),
why don't you get it an post it for all of us?  2) If you can't make any
more constructive comments than "it's an absolute piece of trash" please
don't bother posting them.  I for one don't need to hear or see comments
like that.  Constructive criticisim is one thing, but the comments above
are another thing entirely.


-- 

  Mike Stalnaker  {decvax!grendel,cbosgd!seismo}!dolqci!mike
                  Be wary of strong drink: It can cause you to
	 	  shoot at the tax collector... and miss.

ee163acp@sdcc13.UUCP (DARIN JOHNSON) (03/01/85)

> This version of kermit is an absolute piece of trash. There is a full
> server implementation of kermit available from columbia university.
> 

Ahem...  I can see it now, hundreds of programs not posted for fear of
being ridiculed...

I feel this was a bit uncalled for.  Often people look at sources in
order to get ideas, not just to have a something they can run.  A kermit
source would be useful to discover the protocol without a lot of
research.  Often a poster may want feedback.  If people become
intimidated, programs may start being passed around person-to-person
instead of in the public domain.  (I may get burned for this, but I
that a lot of news and replies on the net are tending to get childish
and unprofessional lately)

   Darin Johnson
     UCSD

mlh@phs.UUCP (Mike Hines) (03/01/85)

Kermit came up without a hitch on my 11/34 V7 system and the
terminal emulator works fine.  Is there something I should know
before attempting to transfer files?
Mike Hines
duke!phs!~mlh

edhall@randvax.UUCP (Ed Hall) (03/04/85)

> This version of kermit is an absolute piece of trash. There is a full
> server implementation of kermit available from columbia university.
> 
> 					Random
> 					Research Triangle Institute
> 					...!mcnc!rti-sel!rcb

Trash, you say?  It works very well!  The version of KERMIT posted has
been used and incrementally refined for a long time.  It is a robust
piece of software.

On the other hand, the new version of KERMIT you mention is still in
preliminary form, and there are still serious bugs.  I have a recent
version (FTP'd from Columbia's system two days ago!), and it tends
to behave very erratically in server mode, quickly becoming unusable.

I'll stick to the older version (still avaliable from Columbia, by the
way) until this one is no longer ``preliminary''.

Those of you with ARPA access can pick up either version from host
``CU20B'' via anonymous FTP.  The old version is ``KER:UX*'', the
newer ``KER:CK*''.  This host is also on a couple of other computer
networks.  (Note: as near as I can tell, the older version at Columbia
is the same as the one posted.)

		-Ed Hall
		decvax!randvax!edhall

rcb@rti-sel.UUCP (Random) (03/04/85)

According to the comments in the code that I have, the person that
posted the kermit in c source is NOT the author. He is apparently
a poor misguided soul that thought that this was the best available 
for UNIX. In fact, the kermit posted is about as mininal an implementation
as it is possible to make and using it will confuse most "full" kermit
implementations (at least the ones that I use). The comment was posted
to try and prevent other users the pain of trying to use this system
when there is a much better one available (which has now been posted).

As to harrassing the author, according to the code I have, the author
of the old version and the author of the new version are the
same person. And the new version is an excellent program. I still
maintain however that the old version is a good example of a bad
hack and should not have been posted.

					Random
					Research Triangle Institute
					...!mcnc!rti-sel!rcb