/* pe7ptb.c */
/* include external declarations */
#include "pe7inc.h"

/*
 *	g n x t f l
 *
 *	Get next file in a file group
 *
 */

gnxtfl()
{
	if (filecount-- == 0)
		return (FALSE); /* If no more, fail */
	if (debug) printf("Gnxtfl 1: filelist = \"%s\"\n",*filelist);
	filnam = *(filelist++);
	return (TRUE);
}

/*
 * d o s t a t s
 *
 * Gather various statistics
 *
 */
dostat(type)
	int type;
{
	ULONG time();
	switch(type)
	{
	case 1:					/* Init Overall totals */
		total.files = 0;
		total.fc = 0;
		total.pli = 0;
		total.plo = 0;
		total.cli = 0;
		total.clo = 0;
		total.time = 0;
		break;
	case 2:					/* Init File totals */
		file.fc = 0;
		file.pli = 0;
		file.plo = 0;
		file.cli = 0;
		file.clo = 0;
		file.time = time();
		break;
	case 3:					/* Accumulate Overall totals */
		file.time = time()-file.time;
		if (vflg)
		{
			printf("%d Bytes   %d Seconds   %d/%d Packets",
				file.fc, file.time, file.plo, file.pli);
			printf("   %d/%d Characters\n", file.clo, file.cli);
		}
		total.fc += file.fc;
		total.pli += file.pli;
		total.plo += file.plo;
		total.cli += file.cli;
		total.clo += file.clo;
		total.time += file.time;
		break;
	case 4:					/* Count total files */
		total.files++;
	}
}
/*
 *  d o p a r
 *
 *  Set parity bit
 *
 */

dopar(ch)
TEXT ch;
{
	TEXT a;

	a=ch;
	if (pflg)					/* True = generate parity */
	{
		a &= 0177;				/* Strip top bit */
		switch (pflg)
		{
		case 'o':			/* Odd parity */
			a |= 0200;		/* Set bit then do even */
		case 'e':			/* Even parity */
			a = (a & 15) ^ ((a >> 4) & 15);
			a = (a &  3) ^ ((a >> 2) &  3);
			a = (a &  1) ^ ((a >> 1) &  1);
			a = ch & 0177 | (a << 7);
		case 's':			/* Space parity */
		default:			/* No parity */
			break;
		case 'm':			/* Mark parity */
			a |= 0200;
		}
	}
	return (a);
}

/*
 *  c h k c h a r
 *
 * Check range of input and return the character or FALSE
 *
 */

TEXT chkchar(c)
TEXT c;
{
	return ((c < 33) || (c > 62 && c < 96) || (c > 126) ? FALSE : c);
}
/*
 *	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).
 *
 */

flushinput()		/* Null version for non-Berkeley Unix */
{
	estty(ttyfd,&ttymode);		/* This flushes the input buffer */
}

/*
 *	Kermit printing routines:
 *
 *	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
 *
 * Like printmsg but will exit after printing the message
 *
 */

Usage(message,a1,a2,a3,a4,a5)
TEXT *message;
{
	TEXT
		cp[BUFSIZE];
	cpystr(&cp, "Kermit usage: ", message, "\n", NULL);
	printf(&cp, a1, a2, a3, a4, a5);
	if (ttyfd > 0)
	  estty (ttyfd,&savemode);	/* Restore the tty to what it was */
	exit(NO);
}
/*
 *	p r i n t m s g
 *
 *	Print message on standard output if not remote.
 *
 */

printmsg(fmt, a1, a2, a3, a4, a5)
TEXT *fmt;
{
	TEXT cp[BUFSIZE];
	cpystr(&cp, "Kermit: ", fmt, "\n", NULL);
	printf(&cp, a1, a2, a3, a4, a5);
}
/*
 *	e r r o r
 *
 *	Print error message.
 *
 *	If remote, send an error packet with the message.
 *
 */

error(fmt, a1, a2, a3, a4, a5)
TEXT *fmt;
{
	TEXT cp[MAXPACKSIZ], msg[80];	/* Some arrays for the strings */
	int len;	/* The length of these arrays */

	convert(&cp, fmt);		/* Convert the format string */
	len = decode(msg, 80, &cp, a1, a2, a3, a4, a5);
	spack('E',n,len,msg);	/* Send the error packet */
	return;
}
/*
 *	c o n v e r t
 *
 *	Convert the UNIX format string to an IDRIS format string
 *
 */

convert(out, in)
TEXT *out, *in;
{
	TEXT	t,
	*cpp;

	cpp = out;			/* Init the buffer pointer */

	while ((t = *cpp++ = *in++) != NULL)
		if (t == '%')
			switch (t = *in++)
			{
			case 'c':
				*cpp++ = 'a';
				*cpp++ = 'c';
				break;
			case 'x':
				*cpp++ = 'h';
			case 'd':
				*cpp++ = 'i';
				break;
			case 's':
				*cpp++ = 'p';
				break;
			case '\0':
				--in;
				break;
			default:
				*cpp++ = t;
			}
}
/*
 *	p r e r r p k t
 *
 *	Print contents of error packet received from remote host.
 *
 */

prerrpkt(msg)
TEXT *msg;
{
	putstr(STDERR,\
		"Kermit: Abort with error from remote host:\n",\
		"        ",msg,"\n",NULL);
	return;
}
/*
 * p r i n t f
 *
 * Print formatted output. Convert from UNIX to IDRIS
 *
 */

printf(msg, a1, a2, a3, a4, a5)
TEXT *msg;
{
	TEXT
		cp[MAXPACKSIZ]; /* Line pointer for the reformatted string */

	convert(&cp, msg);	/* Convert the string */
	putfmt(&cp, a1, a2, a3, a4, a5);
}
/*
 * i n l i n e
 *
 * Input a line (up to break char) from communications line
 *
 */
inline(data)
	TEXT *data;
{
	TEXT t;
	int len, t1;

	t1 = tflg ? XON : MYEOL;
	len = 0;
	do
	{
		if (ioread(&t) <= 0)
		{
			data[len]=MYEOL;
			return(len);
		}
		else
			if (t)
			{
				if (t == SOH)	/* Resync on SOH */
					len = 0;
				else
					data[len++] = t;
			}
	}
	while ((t != t1) && (len < MAXPACKSIZ));

	file.cli += len + 1;
	data[len] = '\0';		/* Terminate input string */

	if (tflg)					/* Turn around flag on? */
		do
			if (ioread(&t) < 0) break;
		while (t != XON);

	return (len);
}
/*
 *	i o r e a d
 *
 *	Read a character from the i/o channel
 *
 */

ioread(t)
TEXT *t;
{
	ULONG time_end,
	time();
	int result;

	if (timint > 0)
	{
		time_end = time() + timint;
		do
			if ((result = read(ttyfd, t, 1)) > 0) break;
		while (time_end >= time());
	}
	else
		result = read(ttyfd, t, 1);
	if (pflg)
		*t &= 0177;				/* Handle parity */
	return (result);
}

/*
 *	x f i l e
 *
 *	Fetch file and send file header
 *
 */

TEXT xfile()
{
	TEXT filnam1[MAXFNAME];		/* A buffer for the file name */
	TEXT *newfilnam;		/* A pointer into the filename */
	TEXT *cp;				/* char pointer */
	int num, len;			/* Packet number, length */
	ULONG time();

	if (numtry++ > MAXTRY) return ('A'); /* If too many tries, give up */
	
	if (fd == NULL)			/* If not already open, */
	{

		/* cpystr(filnam1, filnam, NULL); /* */
		btobemp(filnam1, filnam, MAXFNAME);
		if (filnamcnv)		/* Convert upper case to lower */
			for (cp = &filnam1; *cp != '\0'; cp++)
				*cp = tolower(*cp);

		newfilnam = cp = filnam;

		while (*cp != '\0')	/* Strip leading directories */
			if (*cp++ == '/')
				newfilnam = cp;
		len = cp - newfilnam;	/* Compute length of filename */

		if (debug) printf("Xfile 1: Opening %s for sending.\n",&filnam1);
		/* Open file to be sent */
		if ((fd = fopen(&pfio, filnam1, READ)) == NULL)
		{						/* If bad file pointer, give up */
			error("Cannot open file %s",filnam);
			return ('C');		/* Go back to idle mode */
		}

		if (vflg)
			printmsg("Sending %s as %s", filnam1, newfilnam);
	}
	dostat(2);
	spack('F',n,len,newfilnam); /* Send an F packet */
	switch(rpack(&len,&num,recpkt)) /* What was the reply? */
	{
	case 'N':			/* NAK, just stay in this state, */
		num = (--num<0 ? 63 : num);/* unless it's NAK for next packet */
	case 'Y':
		if (n != num)		/* which is just like an ACK for */ 
		{					/* Wrong packet number */
	case FALSE:				/* Receive failure, stay in F state */
			nxi++;
			return (state);
		}
		nxtpkt();
		dostat(4);
		repeat_count = 0;	/* Reset repeat character counter */
		empty[1-pknum] = 0;	/* Make other buffer empty */
		if ((empty[pknum] = size[pknum] = bufill (packet[pknum])) != EOF)
			return ('E');	/* Switch to data state */
		return ('Z');		/* Must be end of file */

	case 'E':				/* Error packet received */
		prerrpkt(recpkt);	/* Print it out and */
	default:
		return ('A');		/* Something else, just "abort" */
	}
}

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

TEXT sdata()
{
	int num, len;		/* Packet number, length */

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

	/* If the packet has not been acked then do not fill it
		We may need to retransmit it again */
	spack('D',n,size[pknum],packet[pknum]);	/* Send a D packet */
	if (!empty[1-pknum])
		empty[1-pknum] = size[1-pknum] = bufill(packet[1-pknum]);
	switch(rpack(&len,&num,recpkt)) /* What was the reply? */
	{
	case 'N':			/* NAK, just stay in this state, */
		num = (--num<0 ? 63 : num);/* unless it's NAK for next packet */
	case 'Y':
		if (n != num)		/* which is just like an ACK for */ 
		{					/* Wrong packet number */
	case FALSE:				/* Receive failure, stay in D */
			nxi++;
			return (state);
		}
		empty[pknum] = 0;	/* Empty this buffer */
		nxtpkt();
		if (size[pknum] == EOF)
			return ('Z');	/* Send eof packet */
		return ('E');		/* Got data, stay in state E */

	case 'E':				/* Error packet received */
		prerrpkt(recpkt);	/* Print it out and */
	default:
		return ('A');		/* Anything else, "abort" */
	}
}

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

TEXT rdata()
{
	int num, len;			/* Packet number, length */
	ULONG time();

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

	switch(rpack(&len,&num,recpkt)) /* 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,0,0); /* 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 */
		spack('Y',n,0,0);	/* Acknowledge the packet */
		bufemp(recpkt,len); /* Write the data to the file */
		oldtry = numtry;	/* Reset the try counters */
		nxtpkt();
		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(&pfio);		/* Close the file */
		fd = NULL;			/* Say the file is closed */
		nxtpkt();
		dostat(3);
		return ('F');		/* Go back to Receive File state */

	case FALSE:				/* Didn't get packet */
		nxi++;
		spack('N',n,0,0);	/* Return a NAK */
		return (state);		/* Keep trying */

	case 'E':				/* Error packet received */
		prerrpkt(recpkt);	/* Print it out and */
	default:
		return ('A');		/* Some other packet, "abort" */
	}
}

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

TEXT seof()
{
	int num, len;		/* Packet number, length */
	ULONG time();

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

	spack('Z',n,0,0);		/* 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, */
	case 'Y':
		if (n != num)		/* which is just like an ACK for */ 
		{					/* Wrong packet number */
	case FALSE:				/* Receive failure, stay in Z */
			nxi++;
			return (state);
		}
		nxtpkt();
		dostat(3);
		if (debug) printf("Seof 1: Closing input file %s\n",filnam);
		fclose(&pfio);		/* Close the file */
		fd = NULL;			/* Set flag indicating no file open */ 

		if (debug) printf("Seof 2: Looking for next file...\n");
		if (gnxtfl() == FALSE)	/* No more files go? */
			return ('B');	/* if not, break, EOT, all done */
		if (debug) printf("Seof 3: New file is %s\n",filnam);
		return ('T');		/* More files, switch state to T */

	case 'E':				/* Error packet received */
		prerrpkt(recpkt);	/* Print it out and */
	default:
		return ('A');		/* Something else, "abort" */
	}
}

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

TEXT sbreak()
{
	int num, len;			/* Packet number, length */
	ULONG time();

	if (numtry++ > MAXTRY) return ('A'); /* If too many tries "abort" */

	spack('B',n,0,0);		/* 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, */
	case 'Y':
		if (n != num)		/* Which is just like an ACK for */ 
		{					/* Wrong packet number */
	case FALSE:				/* Receive failure, stay in B */
			nxi++;
			return (state);
		}
		nxtpkt();
		return ('C');		/* Switch state to Complete */

	case 'E':				/* Error packet received */
		prerrpkt(recpkt);	/* Print it out and */
	default:
		return ('A');		/* Other, "abort" */
	}
}
/*
 *	s f i n i s h
 *
 *	Send Finish (LOGOUT)
 *
 */

TEXT sfinish()
{
	int num, len;			/* Packet number, length */

	if (numtry++ > MAXTRY) return ('A'); /* If too many tries "abort" */

	spack('G',n,1,"F");		/* Send a GF 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, */
	case 'Y':
		if (n != num)		/* which is just like an ACK for */ 
		{					/* Wrong packet number */
	case FALSE:				/* Receive failure, stay in B */
			nxi++;
			return (state);
		}
		nxtpkt();
		return ('C');		/* Switch state to Complete */

	case 'E':				/* Error packet received */
		prerrpkt(recpkt);	/* Print it out and */
	default:
		return ('A');		/* Other, "abort" */
	}
}

/*
 *	KERMIT utilities.
 *
 */

/*
 *	s p a c k
 *
 *	Send a Packet
 *
 */

spack(type,num,len,data)
TEXT type, *data;
int num, len;
{
	register int i;			/* Character loop counter */
	register TEXT *bufp;	/* Buffer pointer */
	TEXT buffer[100];		/* Packet buffer */
	TEXT temp[12];			/* A buffer for a status message */
	int checksum;

	data[len] = '\0';		/* Null-terminate data to print it */
	if (debug>1)			/* Display outgoing packet */
	{
		printf("Spack 1: type: %c\n",type);
		printf("         num: %d\n",num);
		printf("         len: %d\n",len);
		if (len != 0)
			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++ = dopar(SOH);	/* Packet marker, ASCII 1 (SOH) */
	/* Send character count and set up checksum */
	*bufp++ = dopar(tochar(len + chkt - '0' + 2));
	*bufp++ = dopar(tochar(num));	/* Packet number */
	*bufp++ = dopar(type);	/* Packet type */

	for (i=0; i<len; i++)	/* Loop for all data characters */
		*bufp++ = dopar(data[i]);	/* Get a character */
	*bufp = '\0';			/* Mark end for block check */
	switch (chkt)
	{
	case '1':
		*bufp++ = dopar(tochar(chk1(buffer+1)));
		break;
	case '2':
		checksum = chk2(buffer+1);
		*bufp++ = dopar(tochar((checksum & 07700) >> 6));
		*bufp++ = dopar(tochar(checksum & 077));
		break;
	case '3':
		checksum = chk3(buffer+1);
		*bufp++ = dopar(tochar((checksum & 0170000) >> 12));
		*bufp++ = dopar(tochar((checksum & 07700) >> 6));
		*bufp++ = dopar(tochar(checksum & 077));
		break;
	}

	if (eol)
		*bufp++ = dopar(eol);	/* Extra-packet line terminator */
	*bufp = '\0';

	i = bufp - buffer;
	write(ttyfd, buffer, i); /* Send the packet */
	file.plo++;
	file.clo += i;

	if (vflg)
	{
		if (nxi == nxs)
			write(STDERR,&temp,decode(&temp,12,"%5i/\r",nxo++));
		else
			write(STDERR,&temp,decode(&temp,12,"%5i/%5i\r",
				nxo++, nxs = nxi));
	}
}

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

rpack(len,num,data)
int *len, *num;				/* Packet length, number */
TEXT *data;					/* Packet data */
{
	int chksum, i, j, len1, pbl;
	TEXT t,					/* Current input character */
	type,					/* Packet type */
	cchksum[4],				/* Our (computed) checksum */
	rchksum[4],				/* Checksum received from other host */
	rpacket[MAXPACKSIZ];	/* Receive packet */

	if(!(len1=inline(&rpacket))) return (FALSE);

	i = 0;
	if ((t = rpacket[i++]) == MYEOL) return (FALSE);
	*len = unchar(t);	 /* Character count */

	if ((t = rpacket[i++]) == MYEOL) return (FALSE);
	*num = unchar(t);	/* Packet number */

	if ((type = rpacket[i++]) == MYEOL) return (FALSE);

	pbl = ((type == 'S') || (type == 'I')) ? 1 : type == 'N' ?
		*len - 2 : chkt - '0';
	*len -= pbl + 2;		/* Data length */

	for (j=0; j < *len;)	/* The data itself, if any */
		if ((data[j++] = rpacket[i++]) == MYEOL) return (FALSE);
	data[*len] = '\0';		/* Mark the end of the data */

	/* Fetch the checksum */
	cchksum[0] = '\0';
	cchksum[1] = '\0';
	cchksum[2] = '\0';
	cchksum[3] = '\0';
	rchksum[0] = '\0';
	rchksum[1] = '\0';
	rchksum[2] = '\0';
	rchksum[3] = '\0';
	for (j=0; j < pbl;)	/* The checksum */
	{
		if ((rchksum[j++] = rpacket[i]) == MYEOL) return (FALSE);
		rpacket[i++] = '\0';
	}

	if (debug > 1)			/* Display incoming packet */
	{
		printf("Rpack 2: type: %c\n",type);
		printf("         num: %d\n",*num);
		printf("         len: %d\n",*len);
		if (*len != 0)
			printf("         data: \"%s\"\n",data);
	}
	switch (pbl)
	{
	case 1:
		cchksum[0] = tochar(chk1(&rpacket));
		break;
	case 2:
		chksum = chk2(&rpacket);
		cchksum[0] = tochar((chksum & 07700) >> 6);
		cchksum[1] = tochar(chksum & 077);
		break;
	case 3:
		chksum = chk3(&rpacket);
		cchksum[0] = tochar((chksum & 0170000) >> 12);
		cchksum[1] = tochar((chksum & 07700) >> 6);
		cchksum[2] = tochar(chksum & 077);
	}
	if (!cmpstr(cchksum, rchksum))
	  type = FALSE;

	file.pli++;
	flushinput();
	return (type);			/* All OK, return packet type */
}

/*
 *  n e x t p k
 *
 *  Increment packet number to the next one.  Reset retry counter.
 *
 */

nxtpkt()
{
	pknum = 1 & (n = (n+1)%64);	/* Bump packet count */
	numtry = 0;					/* Start a new counter */
}

/* pe7ptb.c End-of-file */
