/* FROM SOMEONE'S BBS */
#define CPMEOF	26		/* control/z */
#define OVERWRITE 1		/* define for normal overwrite */
#define MAXERRORS 10		/* max number of times to retry one block */
#define OVRWRITIM 10		/* time to pause (none if OVERWRITE defined) */
#define SECSIZE 128		/* cpm sector, transmission block */
#define SENTIMOUT 80		/* timeout time in send */
#define SLEEP	30		/* timeout time in recv */

/* Protocol characters used */

#define SOH	1	/* Start Of Header */
#define EOT	4	/* End Of Transmission */
#define ACK	6	/* ACKnowlege */
#define NAK	0x15	/* Negative AcKnowlege */
#define CAN	0x18	/* Cancel */
#define DEBUG	1

xmodem(mode, filename)
char mode,*filename;
{

	switch (mode){
	case 'r':
		if(rec(filename)==ERROR)
			return(ERROR);
		break;
	case 's':
		if(sen(filename)==ERROR)
			return(ERROR);
		break;
	default:
		return(ERROR);
	}
	return(TRUE);
}

/********** send a file to the remote **********/
sen(tfile)
char	*tfile;
{

	extern	char	retstr[80];
	char checksum, index, blocknumber, errorcount;
	char sector[SECSIZE], chrhldr[10];
	int foo, noteof, timeout();
	int	rtrn,i;
	int	timout,smin,ssec,stime,imin,isec,itime;
	int	inum,inregs[4],outregs[4];

	if((foo=open(tfile,0x8000))==-1) {
		lineout("Can't open file for send!");
		lineout(tfile);
		return(ERROR);
	}
	errorcount = 0;
	blocknumber = 1;

/* wait 80 seconds for NAK to come and then call timeout routine */

	timout=SENTIMOUT;
	inum=0x21;
	inregs[0]=0x2c00;
	sysint(inum,&inregs,&outregs);
	smin=outregs[2]&0xff;
	ssec=(outregs[3]>>8)&0xff;
	stime=(smin*60)+ssec;
	while(TRUE)
		{
		if((rtrn=nechrg())!=0)
			{
			if(((rtrn&=0x0ff)!=NAK)&&(errorcount<MAXERRORS))
				++errorcount;
			else if((rtrn == NAK)||(errorcount == MAXERRORS))
				break;
			}
		inum=0x21;
		inregs[0]=0x2c00;
		sysint(inum,&inregs,&outregs);
		imin=outregs[2]&0xff;
		isec=(outregs[3]>>8)&0xff;
		itime=(imin*60)+isec;
		if(stime>itime)
			itime+=3600;
		if((itime-stime)>timout)
			{
			timeout();
			return(ERROR);
			}
		}
#ifdef DEBUG
	fprintf(stderr, "transmission beginning\r\n");
	fflush(stderr);
#endif
	if (errorcount == MAXERRORS) {
		errorr();
		return(ERROR);
	}
	noteof=1;
	while(noteof)
		{
		for(i=0;i<SECSIZE;i++)
			{
			noteof=read(foo,chrhldr,1);
			if(noteof)
				{
				sector[i]=chrhldr[0];
				}
			else
				{
				sector[i]=CPMEOF;
				break;
				}
			}
		errorcount = 0;
		while (errorcount < MAXERRORS) {
#ifdef DEBUG
			fprintf(stderr, "{%d} ", blocknumber);
			fflush(stderr);
#endif
			chrput(SOH);	/* here is our header */
			chrput(blocknumber);	/* the block number */
			chrput(~blocknumber);	/* & its complement */
			checksum = 0;
			for (index = 0; index < SECSIZE; index++) {
				chrput(sector[index]);
				checksum += sector[index];
			}
			chrput(checksum);	/* tell our checksum */
			if(readbyte(10)==TIMEOUT)
				{
				chrput(CAN);
				lineout("Timeout!!!");
				return(ERROR);
				}
			if (retstr[0] != ACK)
				++errorcount;
			else
				break;
		}
		if (errorcount == MAXERRORS)
			{
			errorr();
			return(ERROR);
			}
		++blocknumber;
	}
	index = 1;
	while (TRUE) {
		chrput(EOT);
		if(readbyte(10)==TIMEOUT)
			{
			chrput(CAN);
			lineout("Timeout!!!");
			return(ERROR);
			}
		if(retstr[0] == ACK)
			{
			break;
			}
	}
	close(foo);
	lineout("Transmission complete.");
}

/********** receive a file **********/
rec(tfile)
char	*tfile;
{
	extern	char	retstr[80];
	extern	int	blocknos;
	char checksum, index, blocknumber, errorcount, character;
	char sector[SECSIZE];
	int foo;
	char not_ch;
	int	rtrn;
	int	timeout,smin,ssec,stime,imin,isec,itime;
	int	inum,inregs[4],outregs[4];

#ifndef OVERWRITE
	if ((foo = open(tfile, 0)) != -1) {
		close(foo);
		lineout("File already exists.);
		return(ERROR);
	}
#endif

	if ((foo = creat(tfile, 0x8002)) == -1) {
		logf("Unable to create XMODEM file...");
		logf(tfile);
		return(ERROR);
	}
	lineout("You have 30 seconds...");
	timeout=SLEEP;
	inum=0x21;
	inregs[0]=0x2c00;
	sysint(inum,&inregs,&outregs);
	smin=outregs[2]&0xff;
	ssec=(outregs[3]>>8)&0xff;
	stime=(smin*60)+ssec;
	while(TRUE)
		{
		inum=0x21;
		inregs[0]=0x2c00;
		sysint(inum,&inregs,&outregs);
		imin=outregs[2]&0xff;
		isec=(outregs[3]>>8)&0xff;
		itime=(imin*60)+isec;
		if(stime>itime)
			itime+=3600;
		if((itime-stime)>timeout)
			{
			break;
			}
		}

#ifdef DEBUG
	fprintf(stderr, "Starting...\r\n");
	fflush(stderr);
#endif
	chrput(NAK);
	errorcount = 0;
	blocknumber = 1;
	blocknos = 0;
	while ((readbyte(10) != TIMEOUT) && ((character = retstr[0]) != EOT)) {
		if (character != SOH) {
#ifdef DEBUG
			fprintf(stderr, "Not SOH\r\n");
			fflush(stderr);
#endif
			if (++errorcount < MAXERRORS)
				goto nakit;
			else
				{
				errorr();
				return(ERROR);
				}
		}
		if(readbyte(10)==TIMEOUT)
			{
			chrput(CAN);
			lineout("Timeout 1!!!");
			return(ERROR);
			}
		character = retstr[0];
		if(readbyte(10)==TIMEOUT)
			{
			chrput(CAN);
			lineout("Timeout 2!!!");
			return(ERROR);
			}
		not_ch = ~retstr[0];
#ifdef DEBUG
		fprintf(stderr, "[%d] ", character);
		fflush(stderr);
#endif
		if (character != not_ch) {
#ifdef DEBUG
			fprintf(stderr, "Blockcounts not ~\r\n");
			fflush(stderr);
#endif
			++errorcount;
			goto nakit;
		}
		if (character != blocknumber) {
#ifdef DEBUG
			fprintf(stderr, "Wrong blocknumber\r\n");
			fflush(stderr);
#endif
			++errorcount;
			goto nakit;
		}
		checksum = 0;
		for (index = 0; index < SECSIZE; index++) {
			if(readbyte(10)==TIMEOUT)
				{
				chrput(CAN);
				lineout("Timeout 3!!!");
				return(ERROR);
				}
			sector[index] = retstr[0];
			checksum += sector[index];
		}
		if(readbyte(10)==TIMEOUT)
			{
			chrput(CAN);
			lineout("Timeout 4!!!");
			return(ERROR);
			}
		if (checksum != retstr[0]) {
#ifdef DEBUG
			fprintf(stderr, "Bad checksum\r\n");
			fflush(stderr);
#endif
			errorcount++;
			goto nakit;
		}
		write(foo, sector, SECSIZE);
		chrput(ACK);
		blocknumber++;
		blocknos++;
		if (!errorcount)
			continue;
nakit:
		chrput(NAK);
	}
	close(foo);
	if(character!=EOT)
		{
		unlink(tfile);
		chrput(CAN);
		lineout("Timeout waiting for EOT!!!");
		return(ERROR);
		}

	chrput(ACK);	/* tell the other end we accepted his EOT	*/
	chrput(ACK);
	chrput(ACK);

	lineout("Completed.");
}


/* give message that we timed out, and then die */
timeout()
{
	chrput(CAN);
	logf("Timed out waiting for NAK from remote system");
}

errorr()
{
	chrput(CAN);
	logf("too many XMODEM errors...aborting");
                                                       
  