#include <ascii.h>
#include "fido.h"
#include <xfbuf.h>
#include "fidomem.h"

static gnxtfl(void);
static kgetc(void);
static kputc(char);
static kopen(char *);
static kcreat(char *);
static kclose(void);
static kerminit(void);
static sendsw(void);
static char sinit(void);
static char sdata(void);
static char seof(void);
static char sbreak(void);
static recsw(void);
static char rinit(void);
static char rfile(void);
static char rdata(void);
static char rpack(int *,int *,char *);
static cmodin(int);
static bufill(char *);
static fillproc(char *,char);
static bufemp(char *,int);
static spar(char *);
static rpar(char *,int);
static prerrpkt(char *);

/*
 *  K e r m i t	 File Transfer Utility
 *
 *  UNIX Kermit, Columbia University, 1981, 1982, 1983, 1984
 *	Bill Catchings, Bob Cattani, Chris Maio, Frank da Cruz,
 *	Alan Crosswell, Jeff Damens
 *
 *  Also:   Jim Guyton, Rand Corporation
 *		Walter Underwood, Ford Aerospace
 *

	T. Jennings 27 Apr 91

	SPACK() is in a seperate module, to accomodate the different
output characteristics of Ptel and ViaTel.

	This is a version 2 compatible version, the original
one (no 8 bit quoting, no repeat count prefixing) was UXKERMIT.C.
Basic v2 algorithms swiped from RFILE and SFILE, but the horrible
boundary condition bugs in those two have been removed, as well
as the "optimization" that didnt work. (The two bytes at the
end of the packet business.)

 */

#define abs(x) ((x)<0?-(x):(x))
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<=(b)?(a):(b))

/* Symbol Definitions */

#define MAXPACKET  	'~' - ' '	/* Maximum packet size */
#define SP		' '		/* ASCII space */

#define MAXTRY		10		/* Times to retry a packet */
#define MYQUOTE		'#'		/* Quote character I will use */
#define MYEBQ		'&'		/* 8th bit quote I will use */
#define MYRPTQ		'~'		/* repeat count prefix I will use */
#define MYPAD		0		/* Number of padding characters I will need */
#define MYPCHAR		0		/* Padding character I need (ZILCH) */
#define MYEOL		'\r'		/* My end of line char */
#define MYTIME		40		/* seconds after which I should be timed out */
#define MAXTIME		94		/* Maximum timeout interval */
#define MINTIME		20		/* Minumum timeout interval */

#define FALSE		0
#define ERROR 		-1
#define FERROR 		-2
#define TIMEOUT 	-1		/* timeout from the modem */

/* 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).
 *
 * unpar:  turns off the parity bit.
 */

#define tochar(ch)  ((ch) + ' ')
#define unchar(ch)  ((ch) - ' ')
#define ctl(ch)	    ((ch) ^ 64 )
#define unpar(ch)   ((ch) & 127)

/* Global Variables */

static int  ebqflg,
	rpt,			/* repeat count */
	rptflg,			/* true if repeat count prefixing */
	pad;			/* How much padding to send */

static char ebq,		/* 8th bit quote */
	rptq,			/* repeat quote */
	padchar,		/* Padding character to send */
	eol,			/* End-Of-Line character to send */
	quotec;			/* Quote character in incoming data */

static int t,			/* current character */
	next,			/* lookahead character */
	retrymax,		/* retry counter, */
	errors,			/* error counter */
	size,			/* Size of present data */
	rpsiz,			/* Maximum receive packet size */
	spsiz,			/* Maximum send packet size */
	txsiz,			/* approx. amt of data per block (fstat()) */
	maxdata,		/* max size of data packet */
	n,			/* Packet number */
	pktnum,			/* packet number for display */
	oc,			/* data transferred from packets */
	filecount;		/* Number of files left to send */

static char kstate,		/* Present state of the automaton */
	cchksum,		/* Our (computed) checksum */
	recpkt[MAXPACKET + 1],	/* Receive packet buffer */
	packet[MAXPACKET + 1];	/* Packet buffer */
	errmsg[SS];		/* Fatal error message, if applicable */

static int fp;			/* File for current disk file */
static char *fn;		/* list of file names to send */
static char *namelist;		/* list of received filenames */
static unsigned listlen;	/* how long it is */
static long byte_count;		/* total bytes sent/recvd */
static char pathname[SS];	/* pathname to receive files */
static char filename[SS];	/* current filename */

/* These are the interfaces to the Fido system, and have to be customized
for whatever system is to be used. */

/* Pick the next name from the list. */

static gnxtfl(void) {

	cpyarg(filename,fn);		/* copy a name, */
	fn= next_arg(fn);		/* for next time */
	return(*filename);
}

/* Get a character, return -1 if read error. */

static kgetc(void) {
char c;

	if (read(fp,&c,1) == 0) return(-1);
	++byte_count;
	return(c);
}
/* write a character. No error check? */

static kputc(c)
char c;
{
	write(fp,&c,1);
	++byte_count;
}

/* Open a file for reading. Returns 0 if OK. */

static kopen(fname)
char *fname;
{
	next= -1;				/* init read ahead */
	rpt= 0;					/* no repeats yet */
	size= 0;				/* empty buffer */
	if (badname(fname)) return(FERROR);	/* device, etc */
	xferstat(1,0L,0,fname);
	fp= open(fname,0);			/* open the file */
	if (fp == -1) {
		xferstat(3,0L,0,"Can't open");
		return(-1);
	}
	return(0);
}
/* Create a file for writing. For Fido, this prepends the path to the
received filename. Returns 0 if OK. */

static kcreat(fname)
char *fname;
{
int f;

	if (badname(fname)) fname[1]= '$';
	xferstat(1,0L,0,fname);
	if (fexist(fname)) {
		xferstat(3,0L,0,"Already exists");
		return(FERROR);
	}
	fp= creat(fname,1);
	if (fp == -1) {
		xferstat(3,0L,0,"Cant create");
		return(FERROR);
	}
	return(0);
}
/* Close a file. If writing, the flush must be done elsewhere. */

static kclose(void)
{
	close(fp);
}

/* Main kermit file transmit function. */

ksend(f,nl,ll)
char *f;		/* file(s) to send */
char *nl;		/* list of sent files */
unsigned ll;		/* how long it is */
{
int n;
long t;

	xferstat(0,0L,0,"");
	kerminit();
	fn= f;				/* ptr to list of input names */
	namelist= nl;
	listlen= ll;
	gnxtfl();			/* prime it with the first name */
	for (t= millisec; (millisec - t) < 30000L;)
		if (modstat()) break;	/* wait for some response */
	n= sendsw();			/* get error code, */
	xferstat(3,0L,0,"Protocol Complete");
	return(n);
}
/* Main kermit receive function. */

krecv(fn,nl,ll)
char *fn;		/* file path to receive to */
char *nl;		/* list of received names */
unsigned ll;		/* how long it is */
{
int n;
	kerminit();
	strip_path(pathname,fn);	/* pathname to put files in */
	namelist= nl;			/* where we keep filenames */
	listlen= ll;			/* how big it is */
	n= recsw();
	xferstat(3,0L,0,"Protocol Complete");
	return(n);
}
/*
 * Kermit initialization 
 *
 */

static kerminit() {

	t= -1;
	next= -1;
	retrymax= MYTIME;
	errors= 0;
	eol = MYEOL;			/* EOL for outgoing packets */
	quotec = MYQUOTE;		/* Standard control-quote char "#" */
	ebq= 'N';			/* no 8 bit quotes unless requested */
	ebqflg= 0;
	rptq= MYRPTQ;			/* repeat prefix */
	rptflg= 1;			/* yes, do it */
	pad = MYPAD;			/* No padding */
	padchar = MYPCHAR;		/* Use null if any padding wanted */
	spsiz= MAXPACKET;
	rpsiz= MAXPACKET;
	txsiz= spsiz * 3 / 2;		/* guess at actual xmitted data */

	maxdata= spsiz - 8;		/* initial room for quote */
	fp = 0;				/* Indicate no file open yet */
	filename[0]= NUL;
	flush(0);
	strcpy(errmsg,"File Not Transferred"); /* default error message */

	totl_files= totl_errors= totl_process= totl_recoveries= 0;
	totl_bytes= 0L;
}


/*
 *  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.
 *
 */

static sendsw(void)
{
	char sinit(), sfile(), sdata(), seof(), sbreak();

	kstate = 'S';			/* Send initiate is the start state */
	n = 0;				/* Initialize message number */
	errors = 0;			/* Say no tries yet */
	while(1)			/* Do this as long as necessary */
	{
	if (errors >= retrymax) kstate= DEL;

	switch(kstate)
	{
		case 'S':
		xferstat(2,0L,0,"Send Init");
		kstate = sinit();  
		break; /* Send-Init */

		case 'F':
		xferstat(2,0L,0,"Send File");
		kstate = sfile();  
		break; /* Send-File */

		case 'D':
		xferstat(2,byte_count,0,"Data");
		kstate = sdata();
		break; /* Send-Data */

		case 'Z':
		xferstat(2,byte_count,0,"End of File");
		kstate = seof();  
		break; /* Send-End-of-File */

		case 'B':
		xferstat(2,0L,0,"Last file sent");
		kstate = sbreak(); 
		break; /* Send-Break */

		case 'C':
		xferstat(2,0L,0,"All files sent");
		return (0);		 /* Complete */

		case 'A':
		case DEL:
		xferstat(3,0L,0,"\"%s\"",errmsg);
		++totl_errors;
		return (ERROR);		 /* "Abort" */

		default:
		xferstat(3,0L,0,"Error");
		++totl_errors;
		return (ERROR);		 /* Unknown, fail */
	}
	}
}


/*
 *  s i n i t
 *
 *  Send Initiate: send this host's parameters and get other side's back.
 */

static char sinit(void)
{
int num, len;				/* Packet number, length */

	if (++errors > retrymax) return('A'); /* If too many tries, give up */

	len= spar(packet);		/* Fill up init info packet */
	spack('S',n,len,packet);	/* Send an S packet */
	switch(rpack(&len,&num,recpkt))	/* What was the reply? */
	{
	case 'N':  return(kstate);	/* NAK, try it again */

	case 'Y':			/* ACK */
		if (n != num)		/* If wrong ACK, stay in S state */
			return(kstate);	/* and try again */
		rpar(recpkt,len);	/* Get other side's init info */

		if (eol == 0) eol = MYEOL;/* Check and set defaults */
		if (quotec == 0) quotec = MYQUOTE;

		errors = 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(kstate);	/* Receive failure, try again */

	default: return('A');		/* Anything else, just "abort" */
	}
 }


/*
 *  s f i l e
 *
 *  Send File Header.
 */

char sfile(void)
{
int num, len;				/* Packet number, length */
char 
	newfilename[SS],		/* Pointer to file name to send */
	*cp,				/* char pointer */
	path[SS];			/* stripped off path */

	if (++errors > retrymax) return('A'); /* If too many tries, give up */
	
	pktnum= 0;			/* number for display */
	byte_count= 0L;			/* bytes sent so far */

	if (fp == 0) {			/* If not already open, */
		if (kopen(filename)) {	/* open the file to be sent */
			return('A');	/* If bad file pointer, give up */
		}
	}
	cp= strip_path(path,filename);	/* clean up name for sending */
	cpyarg(newfilename,cp);		/* copy in name only */
	stoupper(newfilename);		/* all upper case */
	len= strlen(newfilename);	/* its actual length */

	spack('F',n,len,newfilename);	/* 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(kstate);		/* this packet so fall thru to... */

	case 'Y':			/* ACK */
		if (n != num) return(kstate); /* If wrong ACK, stay in F state */
		errors = 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(kstate);	/* Receive failure, stay in F state */

	default: return('A');		/* Something else, just "abort" */
	}
}


/*
 *  s d a t a
 *
 *  Send File Data
 */

static char sdata(void)
{
int num, len;				/* Packet number, length */

	if (++errors > retrymax) 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(kstate);		/* this packet so fall thru to... */
		
	case 'Y':			/* ACK */
		if (n != num) 
			return(kstate); /* If wrong ACK, fail */
		errors = 0;		/* Reset try counter */
		++pktnum;		/* display next packet */
		n = (n+1)%64;		/* Bump packet count */
		size= bufill(packet);	/* size of outgoing packet */
		if (size == 0)		/* 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(kstate);	/* Receive failure, stay in D */

	default: return('A');		/* Anything else, "abort" */
	}
}


/*
 *  s e o f
 *
 *  Send End-Of-File.
 */

static char seof(void)
{
int num, len;			/* Packet number, length */

	if (++errors > retrymax) 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(kstate);		/* this packet so fall thru to... */

	case 'Y':			/* ACK */
		if (n != num) 
			return(kstate); /* If wrong ACK, hold out */
		errors = 0;		/* Reset try counter */
		n = (n+1)%64;		/* and bump packet count */
		++totl_files;
		totl_bytes += byte_count;
		if (strlen(filename) + 1 < listlen) {
			strcat(namelist," "); /* add it to the list of */
			strcat(namelist,filename);
			listlen -= strlen(filename);
			listlen--;	/* completed files */
		}
		kclose();		/* Close the input file */
		fp = 0;			/* Set flag indicating no file open */ 
		gnxtfl();		/* next file ... */
		if (! *filename)
			return('B');	/* if none, break, EOT, all done */
		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(kstate);	/* Receive failure, stay in Z */

	default: return('A');		/* Something else, "abort" */
	}
}


/*
 *  s b r e a k
 *
 * Send Break (EOT)
 */

static char sbreak(void)
{
int num, len;				/* Packet number, length */

	if (++errors > retrymax) 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(kstate);		/* this packet so fall thru to... */

	case 'Y':			/* ACK */
		if (n != num) return(kstate); /* If wrong ACK, fail */
		errors = 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(kstate);	/* Receive failure, stay in B */

	default: return ('A');		/* Other, "abort" */
	}
}


/*
 *  r e c s w
 *
 *  This is the state table switcher for receiving files.
 */

static recsw(void)
{
char rinit(), rfile(), rdata();		/* Use these procedures */

	kstate = 'R';			/* Receive-Init is the start state */
	n = 0;				/* Initialize message number */
	errors = 0;			/* Say no tries yet */

	while(1)
	{
		if (errors >= retrymax) kstate= DEL;	/* dummy */

		switch(kstate)			/* Do until done */
		{
			case 'R':
			xferstat(2,0L,0,"Recv Init");
			kstate = rinit(); 
			break;			/* Receive-Init */

			case 'F':
			xferstat(2,0L,0,"Get filename");
			kstate = rfile(); 
			break;			/* Receive-File */

			case 'D':
			xferstat(2,byte_count,0,"Data");
			kstate = rdata(); 
			break;			/* Receive-Data */

			case 'C':
			xferstat(2,0L,0,"No more files");
			return(0);		/* Complete state */

			case 'A':		/* true abort */
			case DEL:		/* manual abort */
			xferstat(3,0L,0,"\"%s\"",errmsg);
			++totl_errors;
			return(ERROR);		/* "Abort" state */
		}
	}
}

	
/*
 *  r i n i t
 *
 *  Receive Initialization
 */
  
static char rinit(void)
{
int len, num;				/* Packet length, number */

	if (++errors > retrymax) return('A'); /* If too many tries, "abort" */

	switch(rpack(&len,&num,packet))	/* Get a packet */
	{
	case 'S':			/* Send-Init */
		rpar(packet,len);	/* Get the other side's init data */
		len= spar(packet);	/* Fill up packet with my init info */
		spack('Y',n,len,packet);/* ACK with my parameters */
		errors = 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(kstate);		/* Keep trying */

	default:  return('A');		/* Some other packet type, "abort" */
	}
}


/*
 *  r f i l e
 *
 *  Receive File Header
 */

static char rfile(void)
{
int num, len;				/* Packet number, length */
char work[SS];
char *cp;

	pktnum= 0;
	if (++errors > retrymax) return('A'); 	/* "abort" if too many tries */

	switch(rpack(&len,&num,packet))		/* Get a packet */
	{
	case 'S':				/* Send-Init, maybe our ACK lost */
		if (num == ((n==0) ? 63:n-1)) { /* Previous packet, mod 64? */
			len= spar(packet);	/* our Send-Init parameters */
			spack('Y',num,len,packet);
			return(kstate);		/* Stay in this state */

		} else return('A');		/* Not previous packet, "abort" */

	case 'Z':				/* End-Of-File */
		if (num == ((n==0) ? 63:n-1)) {	/* Previous packet, mod 64? */
			spack('Y',num,0,0);
			return(kstate);		/* 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 */
		cp= strip_path(work,packet);	/* strip any path off */
		cpyarg(filename,cp);		/* clean name, no path */
		makeuname(packet,filename);	/* make a full name */
		stoupper(packet);		/* clean it up */
		if (kcreat(packet))		/* Try to open a new file */
			return('A');
		spack('Y',n,0,0);		/* Acknowledge the file header */
		errors = 0;			/* ... */
		byte_count= 0L;
		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(kstate);			/* Keep trying */

	default: return ('A');			/* Some other packet, "abort" */
	}
}

/*
 *	KERMIT utilities.
 */

/*
 *  r d a t a
 *
 *  Receive Data
 */

static char rdata(void)
{
int num, len;				/* Packet number, length */

	if (++errors > retrymax) return('A');	/* "abort" if too many tries */

	switch(rpack(&len,&num,packet)) {		/* Get packet */
	case 'D':					/* Got Data packet */
		if (num != n) {				/* Right packet? */
			if (num == ((n==0) ? 63:n-1)) { /* Else check packet number */
				spack('Y',num,0,0);	/* re-ACK it */
				return(kstate);		/* 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 */
		byte_count += len;		/* byte tally */
		errors = 0;			/* ... */
		++pktnum;			/* display next packet */
		n = (n+1)%64;			/* Bump packet number, mod 64 */
		return('D');			/* Remain in data state */

	case 'F':				/* Got a File Header */
		if (num == ((n==0) ? 63:n-1)) {	/* Else check packet number */
			spack('Y',num,0,0);	/* ACK it again */
			return(kstate);		/* 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. */
		kclose();			/* Close the file */
		totl_bytes += byte_count;
		++totl_files;			/* got another */
		if (strlen(filename) + 1 < listlen) {
			strcat(namelist," "); /* add it to the list of */
			strcat(namelist,filename);
			listlen -= strlen(filename);
			listlen--;	/* completed files */
		}
		fp= 0;
		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(kstate);			/* Keep trying */

	default:  return('A');			/* Some other packet, "abort" */
	}
}

/*
 *  r p a c k
 *
 *  Read a Packet
 */

static char rpack(len,num,data)
int *len, *num;				/* Packet length, number */
char *data;				/* Packet data */
{
int i, done;			/* Data character number, loop exit */
int cs,t;			/* input character */
char 	lastc,			/* for Control-C trapping */
	type,			/* Packet type */
	rchksum;		/* Checksum received from other host */

	cs= 0; lastc= 0;

	for (i= 10; i > 0; i--) {
		if (chkabort()) return('A');
		t= modin(100);			/* get a character, */
		if (t == SOH) break;		/* SOH means a block, exit */
		if ((t == CAN) && (lastc == CAN)) ++cs; /* watch for Control-C's */
		else cs= 0;			/* in a row, */
		lastc= t;
		if (cs > 4) return('A');
	}
	if (t != SOH) return(FALSE);

	done = FALSE;				/* Got SOH, init loop */
	while (!done) {				/* Loop to get a packet */
		cchksum = 0;
		t= cmodin(1000);		/* get character count */
		if (t == SOH) continue;		/* resync if SOH */
		if (t == TIMEOUT) return(FALSE);/* break out if timeout */
		*len = unchar(t)-3;		/* Character count */

		t= cmodin(1000);		/* get character count */
		if (t == SOH) continue;		/* resync if SOH */
		if (t == TIMEOUT) return(FALSE);/* break out if timeout */
		*num = unchar(t);		/* Packet number */

		t= cmodin(1000);		/* get character count */
		if (t == SOH) continue;		/* resync if SOH */
		if (t == TIMEOUT) return(FALSE);/* break out if timeout */
		type = t;			/* Packet type */

/* Put len characters from the packet into the data buffer */

		for (i=0; i < *len; i++) {
			t= cmodin(1000);
			if (t == TIMEOUT) return(FALSE);
			if (t == SOH) break;	/* tested outside */
			data[i]= t;
		}
		if (t == SOH) continue;		/* found SOH inside for() */

		data[*len] = 0;			/* Mark the end of the data */

		t= modin(1000);
		if (t == SOH) continue;		/* resync if SOH */
		if (t == TIMEOUT) return(FALSE);/* timeout */
		rchksum = unchar(t);		/* Convert to numeric */
		done = 1;			/* Got checksum, done */
	}

	cchksum = (((cchksum & 0300) >> 6) + cchksum) & 077; /* final checksum */

	if (cchksum != rchksum) return(FALSE);

	return(type);				/* All OK, return packet type */
}


/*
 * Get a parity adjusted character from the line, add it to the checksum
 * and return it.
 */

static cmodin(n)
int n;
{
char ch;

	ch = modin(1000);		/* Get a character */
	cchksum += ch;			/* Add to the checksum */
	return(ch);
}

/*
 *	b u f i l l
 *
 * Get a bufferful of data to send to the remote.
 *
 */

static bufill(buffer)
char *buffer;
{
	oc= 0;				/* none read yet */
	size= 0;			/* empty packet */
	if (next < 0) t= kgetc();	/* first time through */

	while (t >= 0) {		/* until EOF */
		if (size >= maxdata) break;
		next= kgetc();		/* lookahead */
		fillproc(buffer,t);	/* process it, */
		t= next;		/* shift */
		++oc;
	}
	byte_count += oc;		/* byte tally */
	buffer[size]= NUL;
	return(size);			/* packet size */
}
/*
 *	f i l l p r o c
 *
 * Process a character for an outgoing packet.
 */
static fillproc(buffer,a)
char *buffer,a;
{
char a7;

	if (rptflg) {			/* if 8 bit quoting allowed */

		if (a == next) {	/* repeated character */

/* This byte is the same as the last byte; start (or continue)
repeat counting. If we get to the maximum in a row ('^' - ' ') then
force a new sequence. */

			if (++rpt < 94)	/* Abs. Max. KERMIT rpt count */
				return;

			else {		/* force new sequence */
				buffer[size++]= rptq;
				buffer[size++]= tochar(rpt);
				rpt= 0;	/* fall through for the char */
			}

/* If there were two, then do them as regular characters. This is recursive,
but wont fail since (a != next), thats how we got here, and we set rpt == 0
before the call, and hence never get in the else (if rpt == 1). */

		} else if (rpt == 1) {
			rpt= 0;
			fillproc(buffer,a);
			fillproc(buffer,a);
			return;

/* If there were more than two, then do the repeat prefix. */

		} else if (rpt > 0) {
			buffer[size++]= rptq;
			buffer[size++]= tochar(++rpt);
			rpt= 0;
		}
	}

	a7= a & 0x7f;		/* test only 7 bits */

	if (ebqflg && (a & 0x80)) {
		buffer[size++]= ebq;
		a= a7;
	}

	if ((a7 < ' ') || (a7 == DEL)) {
		buffer[size++]= quotec;
		a= ctl(a);
	}

	if (a7 == quotec) {
		buffer[size++]= quotec;
	}

	if (rptflg && (a7 == rptq)) {
		buffer[size++]= quotec;
	}

	if (ebqflg && (a7 == ebq)) {
		buffer[size++]= quotec;
	}

	buffer[size++]= a;
}


/*
 *	b u f e m p
 *
 * Convert the received packet of data into
 * real life data in our output file. 
 */

static bufemp(buffer,len)
char *buffer;
int len;
{
char a,a7,b8;
int i;

	i= 0;
	while (i < len) {			/* empty the buffer */
		a= buffer[i++];
		if (rptflg) {			/* Repeat processing? */
			if (a == rptq) {	/* Got a flag */
				rpt = unchar(buffer[i++]);
				a = buffer[i++];
			}
		}

		b8 = 0;				/* zap the 8th bit */
		if (ebqflg) {			/* if 8 bit quoting enabled */
			if (a == ebq) {		/* and an 8 bit quote, */
				b8 = 0200;	/* set the 8th bit */
				a = buffer[i++];/* and get the next char */
			}
		}

		if (a == quotec) {		/* do control quotes */
			a = buffer[i++];
			a7 = a & 0x7f;
			if (( (a7 >= '@') && (a7 <= '_')) || (a7 == '?'))
				a= ctl(a);
		}
		a |= b8;			/* set or not set above */

		if (rpt == 0) kputc(a);		/* write the byte */
		else for (; rpt > 0; rpt--) 	/* or bytes */
			kputc(a);
	}
}

/*
 *  s p a r
 *
 *  Fill the data array with my send-init parameters
 *
 */

static spar(data)
char *data;
{
	data[0] = tochar(rpsiz);		/* 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] = quotec;			/* Control-Quote character I send */
	data[6] = ebq;				/* Y or the quote char */
	data[7] = '1';				/* checksum type */
	data[8] = rptq;				/* repeat prefix */
	return(9);				/* size of packet */
}


/*  r p a r
 *
 *  Get the other host's send-init parameters
 *
 */

static rpar(data,len)
char *data;
int len;
{

	rptq= ' ';			/* in case we dont get all the data */
	rptflg= 0;			/* for the optional things */
	ebqflg= 0;

	if (len > 9) len= 9;		/* do what we can */
	switch (len) {
		case 9: rptq = data[8];
		case 8: /* checksum type: ignore */
		case 7: ebq = data[6];
		case 6: quotec = data[5];
		case 5: eol = unchar(data[4]);
		case 4: padchar = ctl(data[3]);
		case 3: pad = unchar(data[2]);
		case 2: retrymax = unchar(data[1]);
		case 1: if (unchar(data[0]) <= MAXPACKET)
				spsiz = unchar(data[0]); 
	}

/* Calculate (guess) amount of data that will be sent per block. This is
for use with fstat() */

	txsiz= spsiz * 3 / 2;		/* two thirds */

/* Keep the retries within reasonaable bounds. */

	if (retrymax > MAXTIME) retrymax= MAXTIME;
	if (retrymax < MINTIME) retrymax= MINTIME;

/* Check for a legal repeat quote; if not legal, dont do it. */

	if (rptq == ' ') rptflg= 0;		/* check for no repeats */
	else rptflg= 1;
	xferstat(3,0L,0,rptflg ? "Repeat prefixing" : "No repeat prefixing");


	if (ebq == 'Y')	ebq= MYEBQ;		/* requested: do it */
	if (
		(ebq < '!') ||			/* control */
		((ebq > '@') && (ebq < '[')) ||	/* A - Z */
		((ebq > '`') && (ebq < '{')) ||	/* a - z */
		(ebq == quotec) ||		/* the quote */
		((ebq == rptq) && rptflg)	/* the repeat quote */
	    ) ebqflg= 0;			/* dont use 8 bit quoting */
	else ebqflg= 1;				/* else do */

	xferstat(3,0L,0,ebqflg ? "8 bit quoting" : "No 8 bit quoting");

/* The maximum size of a packet we can generate is limited to leave enough
room at the end for the max. worst case; when we have repeat count prefixing
enabled, had two bytes in a row at the end of the packet. This ensures
we can fit them in. 3 is the overhead length (mark, number, type), 1 is
the size of the quote. */

	maxdata = spsiz - 3 - 1 - (rptflg ? 2 : 0) - (ebqflg ? 1 : 0);
}

/*
 *  p r e r r p k t
 *
 *  Save the error message from the remote, for later display.
 */
static prerrpkt(msg)
char *msg;
{
int i;

	strcpy(errmsg,"Remote: ");
	for (i= strlen(errmsg); i < (sizeof(errmsg) - 1); i++) 
		errmsg[i]= *msg++;
	errmsg[i]= NUL;
}
