Kermit Source (in C)!!!!!!!!

trio at idis.UUCP trio at idis.UUCP
Mon Feb 25 18:22:40 AEST 1985


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



More information about the Comp.sources.unix mailing list