/*************************************************************
 * vt100 terminal emulator - XMODEM protocol support
 *
 *	     860823 DBW - Integrated and rewrote lots of code
 *	     860815 Steve Drew: readchar inproved with real timeouts
 *	v2.0 860809 DBW - Major rewrite
 *	v1.1 860720 DBW	- Switches, 80 cols, colors, bug fixes
 *	v1.0 860712 DBW	- First version released
 *
 *************************************************************/

#define MODULE_XMODEM 1
#include "vt100.h"

/* forward declarations for LATTICE */
void sendstring();
void sendchar();

/************************************************************
* Send a string (using sendchar below)
************************************************************/

void sendstring(s)
char *s;
    {
    char c;

    while ((c = *s++) != '\000') sendchar(c);
    }

/**************************************************************/
/* send char and read char functions for the xmodem function */
/************************************************************/
void sendchar(ch)
int ch;
    {
    rs_out[0] = ch & 0xFF;
    DoIO(Write_Request);
    }

int readchar()
    {
    int rd,ch;
    
    Timer.tr_time.tv_secs = ttime;
    Timer.tr_time.tv_micro = 0;
    SendIO((char *) &Timer.tr_node);
    
    rd = FALSE;
    while (rd == FALSE)  
        {	
	Wait((1L << Read_Request->IOSer.io_Message.mn_ReplyPort->mp_SigBit) |
            ( 1L << mywindow->UserPort->mp_SigBit) |
            ( 1L << Timer_Port->mp_SigBit));
	if (CheckIO(Read_Request))
            {
	    WaitIO(Read_Request);
	    ch=rs_in[0];
	    rd = TRUE;
	    BeginIO(Read_Request);
	    }
	if (NewMessage=(struct IntuiMessage *)GetMsg(mywindow->UserPort)) {
	   if ((NewMessage->Class == RAWKEY) && (NewMessage->Code == 69))
	         {
                 AbortIO((char *) &Timer);
                 Wait (1L << Timer_Port->mp_SigBit);
	         if (want_message) 
	           emits("\nUser aborted transfer\n");
	         timeout = USERABORT;
                 return('\0');
	         }
            continue;
            }
        if (rd == FALSE && CheckIO(&Timer)) {
            if (want_message)
              emits("\nTimeout waiting for character\n");
            timeout = TIMEOUT;
            return('\0');
            }
	}     /* end while */
    AbortIO((char *) &Timer);
    Wait (1L << Timer_Port->mp_SigBit);
    timeout = GOODREAD;
    return(ch & 0xFF);
    }

/**************************************/
/* xmodem send and recieve functions */
/************************************/

int XMODEM_Read_File(file)
char *file;
    {
    int firstchar, sectnum, sectcurr, sectcomp, errors, errorflag;
    unsigned int checksum, j, bufptr;
    char numb[10];
    bytes_xferred = 0L;
    ttime = TTIME_SHORT;
    want_message = TRUE; /* tell readchar to print any error msgs */

    if ((fd = creat(file, 0)) < 0)
	{
	emits("Cannot Open File\n");
	return FALSE;
	}
    else
    emits("Receiving File\n\nType <ESC> to abort transfer\n");

    sectnum = errors = bufptr = 0;
    sendchar(NAK);
    firstchar = 0;
    while (firstchar != EOT && errors != ERRORMAX)
	{
	errorflag = FALSE;

	do {                                    /* get sync char */
	    firstchar = readchar();
	    if (timeout != GOODREAD) {
		if (timeout == USERABORT || errors++ == ERRORMAX)
		    return FALSE;
		}
	    } while (firstchar != SOH && firstchar != EOT);

	if  (firstchar == SOH)
	    {
	    emits("Getting Block ");
	    sprintf(numb, "%d", sectnum);
	    emits(numb);
	    emits("...");
	    sectcurr = readchar();
	    if (timeout != GOODREAD) return FALSE;
	    sectcomp = readchar();
	    if (timeout != GOODREAD) return FALSE;
	    if ((sectcurr + sectcomp) == 255)
		{
		if (sectcurr == ((sectnum + 1) & 0xff))
		    {
		    checksum = 0;
		    for (j = bufptr; j < (bufptr + SECSIZ); j++)
			{
			bufr[j] = readchar();
			if (timeout != GOODREAD) return FALSE;
			checksum = (checksum + bufr[j]) & 0xff;
			}
		    if (checksum == readchar() && timeout == GOODREAD)
			{
			errors = 0;
			sectnum++;
			bufptr += SECSIZ;
			bytes_xferred += SECSIZ;
			emits("verified\n");
			if (bufptr == BufSize)
			    {
			    if (write(fd, bufr, BufSize-128) == EOF)
				{
				emits("\nError Writing File\n");
				return FALSE;
				}
			    bufptr = 128;
			    for (j = 0; j < 128; j++)
				bufr[j] = bufr[(BufSize-128)+j];
			    }
			sendchar(ACK);
			}
		    else
			{
			errorflag = TRUE;
			if (timeout == USERABORT) return FALSE;
			}
		    }
		else
		    {
		    /* got a duplicate sector */	
		    if (sectcurr == (sectnum & 0xff))
			{
			/* wait until we time out for 5secs */
			do {
			    readchar();
			    } while (timeout == GOODREAD);
			if (timeout == USERABORT) return FALSE;
			emits("\nReceived Duplicate Sector\n");
			sendchar(ACK);
			}
		    else errorflag = TRUE;
    		    }
		}
	    else errorflag = TRUE;
	    }
	if (errorflag == TRUE)
	    {
	    errors++;
	    emits("\nError\n");
	    sendchar(NAK);
	    }
	}        /* end while */
    if ((firstchar == EOT) && (errors < ERRORMAX))
	{
	sendchar(ACK);
	while (bufptr > 0 && (bufr[--bufptr] == 0x00 ||
			      bufr[bufptr]   == 0x1A)) ;
	write(fd, bufr, ++bufptr);
	close(fd);
	return TRUE;
	}
    return FALSE;
    }

int XMODEM_Send_File(file)
char *file;
    {
    int sectnum, bytes_to_send, size, attempts, c;
    unsigned checksum, j, bufptr;
    char numb[10];
    bytes_xferred = 0;
    ttime = TTIME_LONG;
    want_message = TRUE; /* tell readchar to print any error msgs */

    if ((fd = open(file, 0)) < 0) {
	emits("Cannot Open Send File\n");
	return FALSE;
	}
    else
    emits("Sending File\n\nType <ESC> to abort transfer\n");
    attempts = 0;
    sectnum = 1;
    /* wait for sync char */
    j=1;
    while (((c = readchar()) != NAK) && (j++ < ERRORMAX))
	if (timeout == USERABORT) return(FALSE);
    if (j >= (ERRORMAX))
	{
	emits("\nReceiver not sending NAKs\n");
	return FALSE;
	}

    while ((bytes_to_send = read(fd, bufr, BufSize)) && attempts != RETRYMAX)
	{
	if (bytes_to_send == EOF)
	    {
	    emits("\nError Reading File\n");
	    return FALSE;
	    }

	bufptr = 0;
	while (bytes_to_send > 0 && attempts != RETRYMAX)
	    {
	    attempts = 0;
	    emits("Block ");
	    sprintf(numb, "%d ", sectnum);
	    emits(numb);
	    do {
		emits(".");    
		sendchar(SOH);
		sendchar(sectnum);
		sendchar(~sectnum);
		checksum = 0;
		size = SECSIZ <= bytes_to_send ? SECSIZ : bytes_to_send;
		bytes_to_send -= size;
		for (j = bufptr; j < (bufptr + SECSIZ); j++)
		if (j < (bufptr + size)) {
		    sendchar(bufr[j]);
		    checksum += bufr[j];
		    }
		else sendchar(0);
		sendchar(checksum);
		attempts++;
		c = readchar();
		if (timeout == USERABORT) {emits("\n"); return FALSE;}
		} while ((c != ACK) && (attempts != RETRYMAX));
	    bufptr += size;
	    bytes_xferred += size;
	    emits(" sent\n");
	    sectnum++;
	    }
	}
    close(fd);
    if (attempts == RETRYMAX)
	{
	emits("\nNo Acknowledgment Of Sector, Aborting\n");
	return FALSE;
	}
    else
	{
	attempts = 0;
	do {
	    sendchar(EOT);
	    attempts++;
	    } while ((readchar() != ACK) &&
		     (attempts != RETRYMAX) &&
		     (timeout != USERABORT)) ;
	if (attempts == RETRYMAX)
	    emits("\nNo Acknowledgment Of End Of File\n");
	}
    return TRUE;
    }

