/*************************************************************
 * 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;
    }

