/*************************************************************
 * vt100 terminal emulator - XMODEM protocol support
 *
 *	v2.6 870227 DBW - bug fixes for all the stuff in v2.5
 *	v2.5 870214 DBW - more additions (see readme file)
 *	v2.4 861214 DBW - lots of fixes/additions (see readme file)
 *	v2.3 861101 DBW - minor bug fixes
 *	v2.2 861012 DBW - more of the same
 *	v2.1 860915 DBW - new features (see README)
 *	     860901 ACS - Added Parity and Word Length and support code
 *	     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
 *
 *************************************************************/

#include "vt100.h"

int enablexon = TRUE;

static unsigned long parity_settings[4] = {
    0x96696996,
    0x69969669,
    0x69969669,
    0x96696996 };

/************************************************************
* 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;
    {
    int doxon,i,j,k;

    doxon = enablexon;
    if (doxon) No_XON();
    switch (p_parity) {
	case 0:	/* no parity */
	rs_out[0] = ch & 0xFF;
	break;

	case 1: /* mark */
	rs_out[0] = (ch & 0x7F) | 0x80;
	break;

	case 2: /* space */
	rs_out[0] = ch & 0x7F;
	break;
	
	case 3:	/* even */
	case 4: /* odd  */
        i     = (ch >> 5) & 0x3;
	j     = ch & 0x1F;
	k     = ((parity_settings[i] >> j) & 0x1) << 7;
	if (p_parity == 3)			/* even parity */
	    rs_out[0] = (ch & 0x7F) | k;
	else					/* odd parity */
	    rs_out[0] = (ch & 0x7F) | (k ^ 0x80);
	}
    DoIO(Write_Request);
    if (doxon) Do_XON();
    }

/* send a break to the host */
void sendbreak() {
    AbortIO(Read_Request);
    Read_Request->IOSer.io_Command = SDCMD_BREAK;
    DoIO(Read_Request);
    Read_Request->IOSer.io_Command = CMD_READ;
    SendIO(Read_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;
	    SendIO(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);
	         req("ERROR:","User aborted transfer",0);
	         timeout = USERABORT;
                 return('\0');
	         }
            continue;
            }
        if (rd == FALSE && CheckIO(&Timer)) {
            req("ERROR","Timeout waiting for character",0);
            timeout = TIMEOUT;
            return('\0');
            }
	}     /* end while */
    AbortIO((char *) &Timer);
    Wait (1L << Timer_Port->mp_SigBit);
    timeout = GOODREAD;
    return(ch & (p_parity == 0 ? 0xFF : 0x7F));
    }

void No_XON() {

    /* turn off XON/XOFF processing */
    enablexon = FALSE;
    Write_Request->io_SerFlags |= SERF_XDISABLED;
    Write_Request->IOSer.io_Command = SDCMD_SETPARAMS;
    DoIO(Write_Request);
    Write_Request->IOSer.io_Command = CMD_WRITE;
    }

void Do_XON() {
    /* turn on XON/XOFF processing */
    enablexon = TRUE;
    Write_Request->io_SerFlags &= ~SERF_XDISABLED;
    Write_Request->IOSer.io_Command = SDCMD_SETPARAMS;
    DoIO(Write_Request);
    Write_Request->IOSer.io_Command = CMD_WRITE;
    }

/**************************************/
/* 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 scrstr[40],scrstr2[40];
    bytes_xferred = 0L;
    ttime = TTIME_SHORT;

    if ((fd = creat(file, 0)) < 0)
	{
	req("XMODEM Can't Open File:",file,0);
	return FALSE;
	}
    else
    strcpy(scrstr,"XMODEM Receive, <esc> to abort");
    req(scrstr,"",0);

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

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

	if  (firstchar == SOH)
	    {
	    sprintf(scrstr2,"Getting Block %4d...",sectnum);
	    req(scrstr,scrstr2,0);
	    sectcurr = readchar();
	    if (timeout != GOODREAD) { Do_XON(); return FALSE; }
	    sectcomp = readchar();
	    if (timeout != GOODREAD) { Do_XON(); 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) { Do_XON(); return FALSE; }
			checksum = (checksum + bufr[j]) & 0xff;
			}
		    if (checksum == readchar() && timeout == GOODREAD)
			{
			errors = 0;
			sprintf(scrstr2,"Block %4d verified",sectnum);
			sectnum++;
			bufptr += SECSIZ;
			bytes_xferred += SECSIZ;
			req(scrstr,scrstr2,0);
			if (bufptr == BufSize)
			    {
			    if (write(fd, bufr, BufSize-128) == EOF)
				{
				req(scrstr,"Error Writing File",0);
				Do_XON();
				return FALSE;
				}
			    bufptr = 128;
			    for (j = 0; j < 128; j++)
				bufr[j] = bufr[(BufSize-128)+j];
			    }
			sendchar(ACK);
			}
		    else
			{
			errorflag = TRUE;
			if (timeout == USERABORT) { Do_XON(); 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) {
			    Do_XON();
			    return FALSE;
			    }
			req(scrstr,"Received Duplicate Sector",0);
			sendchar(ACK);
			}
		    else errorflag = TRUE;
    		    }
		}
	    else errorflag = TRUE;
	    }
	if (errorflag == TRUE)
	    {
	    errors++;
	    req(scrstr,"Error",0);
	    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);
	Do_XON();
	return TRUE;
	}
    Do_XON();
    return FALSE;
    }

int XMODEM_Send_File(file)
char *file;
    {
    int sectnum, bytes_to_send, size, attempts, c;
    unsigned checksum, j, bufptr;
    char scrstr[40],scrstr2[40];
    bytes_xferred = 0;
    ttime = TTIME_LONG;

    if ((fd = open(file, 0)) < 0) {
	req("XMODEM","Cannot Open Send File",0);
	return FALSE;
	}
    else
    strcpy(scrstr,"XMODEM Send, <esc> to abort");
    req(scrstr,"",0);
    attempts = 0;
    sectnum = 1;
    No_XON();
    /* wait for sync char */
    j=1;
    while (((c = readchar()) != NAK) && (j++ < ERRORMAX))
	if (timeout == USERABORT) { Do_XON(); return(FALSE); }
    if (j >= (ERRORMAX))
	{
	req(scrstr,"Receiver not sending NAKs",0);
	Do_XON();
	return FALSE;
	}

    while ((bytes_to_send = read(fd, bufr, BufSize)) && 
	    attempts != RETRYMAX)
	{
	if (bytes_to_send == EOF)
	    {
	    req(scrstr,"Error Reading File",0);
	    Do_XON();
	    return FALSE;
	    }

	bufptr = 0;
	while (bytes_to_send > 0 && attempts != RETRYMAX)
	    {
	    attempts = 0;
	    sprintf(scrstr2,"Sending block %4d",sectnum);
	    do {
		req(scrstr,scrstr2,0);
		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) {
		    req(scrstr,"ABORTED",0);
		    Do_XON();
		    return FALSE;
		    }
		} while ((c != ACK) && (attempts != RETRYMAX));
	    bufptr += size;
	    bytes_xferred += size;
	    sprintf(scrstr2,"Sent block %4d",sectnum);
	    req(scrstr,scrstr2,0);
	    sectnum++;
	    }
	}
    close(fd);
    if (attempts == RETRYMAX)
	{
	req(scrstr,"No Acknowledgment, ABORTING",0);
	Do_XON();
	return FALSE;
	}
    else
	{
	attempts = 0;
	do {
	    sendchar(EOT);
	    attempts++;
	    } while ((readchar() != ACK) &&
		     (attempts != RETRYMAX) &&
		     (timeout != USERABORT)) ;
	if (attempts == RETRYMAX)
	    req(scrstr,"NO END OF FILE",0);
	}
    Do_XON();
    return TRUE;
    }

/* allow for multi file xfers separated by commas under
    kermit and XMODEM */

void multi_xfer(name,mode,do_send)
char *name;
int (*mode)();
int do_send;
    {
    int done = 0;
    int status;
    char *p;
    
    if (name[0] == '$' && name[1] == '\0') {
	saybye();
	return;
	}
    p = name;
    while(*p == ' ') p++;
    while(*p && *p != ',' && *p != ' ') p++;
    if (*p == '\0')   done = TRUE;
    else	      multi = 1;
    *p = '\0';

    status = ((*mode)(name, multi));
    if (status == FALSE) close(fd);
    if (!done && timeout != USERABORT) multi_xfer(++p, mode, do_send);
    else emit('\n');
    server = 0;
    multi = 0;
    }

