XMSEND.C

21.9 KB 13fb80b010eca1cf…
/*--------------------------------------------------------------------------*/
/*                                                                          */
/*                                                                          */
/*      ------------         Bit-Bucket Software, Co.                       */
/*      \ 10001101 /         Writers and Distributors of                    */
/*       \ 011110 /          Freely Available<tm> Software.                 */
/*        \ 1011 /                                                          */
/*         ------                                                           */
/*                                                                          */
/*  (C) Copyright 1987-91, Bit Bucket Software Co., a Delaware Corporation. */
/*                                                                          */
/*                                                                          */
/*                  This module was written by Bob Hartman                  */
/*                                                                          */
/*                                                                          */
/*                 BinkleyTerm Xmodem Sender State Machine                  */
/*                                                                          */
/*                                                                          */
/*    For complete  details  of the licensing restrictions, please refer    */
/*    to the License  agreement,  which  is published in its entirety in    */
/*    the MAKEFILE and BT.C, and also contained in the file LICENSE.250.    */
/*                                                                          */
/*    USE  OF THIS FILE IS SUBJECT TO THE  RESTRICTIONS CONTAINED IN THE    */
/*    BINKLEYTERM  LICENSING  AGREEMENT.  IF YOU DO NOT FIND THE TEXT OF    */
/*    THIS  AGREEMENT IN ANY OF THE  AFOREMENTIONED FILES,  OR IF YOU DO    */
/*    NOT HAVE THESE FILES,  YOU  SHOULD  IMMEDIATELY CONTACT BIT BUCKET    */
/*    SOFTWARE CO.  AT ONE OF THE  ADDRESSES  LISTED BELOW.  IN NO EVENT    */
/*    SHOULD YOU  PROCEED TO USE THIS FILE  WITHOUT HAVING  ACCEPTED THE    */
/*    TERMS  OF  THE  BINKLEYTERM  LICENSING  AGREEMENT,  OR  SUCH OTHER    */
/*    AGREEMENT AS YOU ARE ABLE TO REACH WITH BIT BUCKET SOFTWARE, CO.      */
/*                                                                          */
/*                                                                          */
/* You can contact Bit Bucket Software Co. at any one of the following      */
/* addresses:                                                               */
/*                                                                          */
/* Bit Bucket Software Co.        FidoNet  1:104/501, 1:343/491             */
/* P.O. Box 460398                AlterNet 7:491/0                          */
/* Aurora, CO 80046               BBS-Net  86:2030/1                        */
/*                                Internet f491.n343.z1.fidonet.org         */
/*                                                                          */
/* Please feel free to contact us at any time to share your comments about  */
/* our software and/or licensing policies.                                  */
/*                                                                          */
/*--------------------------------------------------------------------------*/

/* Include this file before any other includes or defines! */

#include "includes.h"

void Build_Header_Block (XMARGSP args, char type)
{
    struct FILEINFO dta;
    SEADATAP ttmp;

    (void) dfind (&dta, args->filename, 0);
    args->save_header = type;
    ttmp = (SEADATAP) &(args->header);

    (void) memset (ttmp, 0, sizeof (XMDATA));
    ttmp->header = type;
    ttmp->block_num = 0;
    ttmp->block_num_comp = 0xff;
    ttmp->filelength = args->filelen;
    (void) strncpy (ttmp->sendingprog, xfer_id, 14);
    if (type == SYN)
        {
        (void) memset (ttmp->filename, ' ', 16);
        ttmp->timedate = dta.time;
        /* This is the CRC bit in the TeLink header */
        ttmp->Resync = 1;
        }
    else
        {
        ttmp->timedate = args->save_filetime.oneword.timedate;
        ttmp->SLO = (unsigned char) (((cur_baud.rate_value >= 9600) && !no_overdrive)? 1 : 0);
        ttmp->Resync = (unsigned char) (no_resync ? 0 : 1);
        ttmp->MACFLOW = 1;
        }
    if (args->temp_name != NULL)
        (void) strncpy (ttmp->filename, args->temp_name, strlen (args->temp_name));
    else
        (void) strncpy (ttmp->filename, (char *) (dta.name), strlen (dta.name));
}

void XSSetVars (XMARGSP);
int XSInit (XMARGSP, int);
int XSEnd (XMARGSP, int);
int XSXmtStart (XMARGSP);
int XSXmTeStrt (XMARGSP);
int XSCheckACK (XMARGSP);
int XSSendBlk (XMARGSP);
int XSWaitEnd (XMARGSP);

STATES Xmodem_Sender[] = {
   { "XSInit", XSInit },
   { "XSEnd",  XSEnd },
   { "XS0",    XSXmtStart },
   { "XS0T",   XSXmTeStrt },
   { "XS1",    XSCheckACK },
   { "XS2",    XSSendBlk },
   { "XS3",    XSWaitEnd }
};

int XSInit (XMARGSP args, int start_state)
{
    struct stat st;
    char junkbuff[100];

    /* Get the file information */
    if (stat (args->filename, &st))
        {
        /* Print error message */
        return (OPEN_ERR);
        }

    if ((args->file_pointer = share_fopen (args->filename, "rb", DENY_WRITE)) == NULL)
        {
        /* Print error message */
        return (OPEN_ERR);
        }

    /* Get important information out of it */
    args->filelen = st.st_size;
    args->LastBlk = (st.st_size + 127) / 128;
    args->save_filetime.oneword.timedate = st.st_atime;
    args->prev_bytes = 0L;
    args->tot_errs = 0;

    (void) sprintf (junkbuff, MSG_TXT(M_SEND_MSG), args->LastBlk, args->filename, st.st_size);
    if (un_attended && fullscreen)
        {
        clear_filetransfer ();
        sb_move (file_hWnd, 1, 2);
        FlLnModeSet( FILE_LN_2, 1 );
        sb_puts( GetDlgItem( file_hWnd, FILE_LN_1 ), junkbuff );
        elapse_time ();
        sb_show ();
        }
    else
       {
       status_line ("+%s", junkbuff);
       (void) printf ("\n");
       }

    locate_y = wherey ();
    locate_x = wherex ();

    /* Start the throughput calculations */
    throughput (0, 0L);
    return (start_state);
}

int XSEnd (XMARGSP args, int cur_state)
{
    args->result = cur_state;

    /* Close file */
    (void) fclose (args->file_pointer);

    if (args->tot_errs > 3)
        status_line (MSG_TXT(M_CORRECTED_ERRORS), args->tot_errs, args->LastBlk);

    /* Log that we sent it */
    if (cur_state == SUCCESS)
        {
        throughput (1, (unsigned long) (args->filelen - args->prev_bytes));
        status_line ("%s: %s", MSG_TXT(M_FILE_SENT), args->filename);
        update_files (1);
        }

    return (cur_state);
}

void XSSetVars (XMARGSP args)
{
    if (no_sealink)
        {
        args->options.SLO = 0;
        args->options.Resync = 0;
        }
    else
        {
        args->options.SLO = ((cur_baud.rate_value >= 9600) && !no_overdrive) ? 1 : 0;
        args->options.Resync = (~no_resync) & 1;
        }

    args->options.SEAlink = 0;
    args->SendBLK = 1;
    args->curr_byte = 0L;
    args->NextBLK = 1;
    args->ACKST = 0;
    args->ACKBLK = -1L;
    args->Window = 1;
    args->ACKsRcvd = 0;
    args->NumNAK = 0;
    args->T1 = timerset (3000);
}

int XSXmtStart (XMARGSP args)
{
    XSSetVars (args);
    Build_Header_Block (args, SOH);
    return (XS1);
}

int XSXmTeStrt (XMARGSP args)
{
    XSSetVars (args);
    Build_Header_Block (args, SYN);
    return (XS1);
}

int XSCheckACK (XMARGSP args)
{
    Check_ACKNAK (args);
    return (XS2);
}

int XSSendBlk (XMARGSP args)
{
    if (!CARRIER)
        return (CARRIER_ERR);

    if (got_ESC ())
        {
        status_line (MSG_TXT(M_KBD_MSG));
        return (KBD_ERR);
        }

    if ((args->NumNAK > 4) && (args->SendBLK == 0))
        {
        if (args->save_header == SOH)
            return (XS0T);
        else
            {
            args->NumNAK = 0;
            ++(args->ACKBLK);
            ++(args->SendBLK);
            return (XS2);
            }
        }

    if (args->NumNAK > 10)
        {
        /* Too Many Errors */
        return (SEND_RETRY_ERR);
        }

    if (timeup (args->T1))
        {
        /* Fatal Timeout */
        return (SEND_TIMEOUT);
        }

    if (args->SendBLK > (args->LastBlk + 1))
        return (XS3);

    if (args->SendBLK > (args->ACKBLK + args->Window))
        {
        time_release ();
        return (XS1);
        }

    if (args->SendBLK == (args->LastBlk + 1))
        {
        SENDBYTE (EOT);
        ++(args->SendBLK);
        args->T1 = timerset (3000);
        show_sending_blocks (args);
        time_release ();
        return (XS1);
        }

    /*
        Increment the block count before sending because we read the next
        block immediately after sending this block.  On error free connects
        we have a big net win because we never do a seek, and while we are
        sending one block, we read the next.  If we do get errors, then we
        have to seek back to the previous block, and that will be a bother.
        With today's phone lines and modems, we'll assume error free is more
        often than not, and take our chances.
    */
    if (args->options.SLO && args->options.SEAlink)
        {
        args->ACKBLK = args->SendBLK;
        }

    ++(args->SendBLK);
    args->curr_byte += 128L;
    Send_Block (args);
    args->T1 = timerset (6000);
    return (XS1);
}

int XSWaitEnd (XMARGSP args)
{
    show_sending_blocks (args);

    if (args->ACKBLK < (args->LastBlk + 1))
        {
        time_release ();
        return (XS1);
        }

    if (!CARRIER)
        return (CARRIER_ERR);

    return (SUCCESS);
}

int SEAlink_Send_File (char *filename, char *sendname)
{
    XMARGS xm;

    xm.filename = filename;
    xm.temp_name = sendname;
    return (state_machine (Xmodem_Sender, &xm, XS0));
}

int Xmodem_Send_File (char *filename, char *sendname)
{
    return (SEAlink_Send_File (filename, sendname));
}

int Telink_Send_File (char *filename, char *sendname)
{
    XMARGS xm;

    xm.filename = filename;
    xm.temp_name = sendname;
    return (state_machine (Xmodem_Sender, &xm, XS0T));
}

void Get_Block (XMARGSP args)
{
    XMDATAP xtmp;

    if (args->SendBLK == 0)
        {
        Build_Header_Block (args, args->save_header);
        args->NextBLK = -1L;
        return;
        }

    xtmp = (XMDATAP) &(args->header);

    /* Set up buffer as all ^Zs for EOF */
    (void) memset (xtmp, SUB, sizeof (XMDATA));

    /* Now set up the header stuff */
    xtmp->header = SOH;
    xtmp->block_num = (unsigned char) (args->SendBLK & 0xff);
    xtmp->block_num_comp = (unsigned char)~xtmp->block_num;

    if (args->NextBLK != args->SendBLK)
        {
        (void) fseek (args->file_pointer, (args->SendBLK - 1) * 128, SEEK_SET);
        }

    args->NextBLK = args->SendBLK + 1;

    /* Can we read any data? */
    if (fread ((char *) xtmp->data_bytes, 1, 128, args->file_pointer) <= 0)
        return;

    /* Looks good */
    return;
}

void Send_Block (XMARGSP args)
{
    if (args->header == SYN)
        {
        Data_Check ((XMDATAP) &(args->header), CHECKSUM);
        }
    else
        {
        Data_Check ((XMDATAP) &(args->header), args->options.do_CRC ? CRC : CHECKSUM);
        }

    if ((!(args->options.do_CRC)) || (args->header == SYN))
        {
        SENDCHARS ((char *) &(args->header), sizeof (XMDATA) - 1, 1);
        }
    else
        {
        SENDCHARS ((char *) &(args->header), sizeof (XMDATA), 1);
        }

    UNBUFFER_BYTES ();

    show_sending_blocks (args);

    Get_Block (args);
}

char *show_num (XMARGSP args, long b)
{
    char               *Rtn;

    Rtn = "EOT";
    if (b > args->LastBlk)
        {
        if (!(fullscreen && un_attended))
            (void) cputs (Rtn);
        }
    else if (b >= 0L)
        {
        Rtn = ultoa (((unsigned long) b), e_input, 10);
        if (!(fullscreen && un_attended))
            (void) cputs (Rtn);
        }
    return Rtn;
}

void show_sending_blocks (XMARGSP args)
{
    char *TmpPtr = (char *)&happy_compiler;
    char j[100];
    int i;
    long k;

    k = args->filelen - args->curr_byte;
    if (k < 0L)
        k = 0L;

    i = (int) ((k * 10 / cur_baud.rate_value * 100 /
        ((args->save_header == SOH) ? 94 : 70) + 59) / 60);
    (void) sprintf (j, "%3d min", i);

    if (args->options.SLO)
        {
        if ((!((args->SendBLK - 1) & 0x1f)) || ((args->SendBLK - 1) > args->LastBlk))
            {
            if (fullscreen && un_attended)
                {
                elapse_time();
                sb_move (file_hWnd, 2, 2);
                TmpPtr = show_num (args, args->SendBLK - 1);
                sb_puts( GetDlgItem( file_hWnd, FILE_LN_2 + GD_TOTAL ), 
                         TmpPtr );
#ifndef MILQ
                (void) sb_putc (file_hWnd, ':');
#endif
                TmpPtr = show_num (args, args->ACKBLK);
                sb_puts( GetDlgItem( file_hWnd, FILE_LN_2 + GD_SIZE ),
                         TmpPtr );
                sb_puts( GetDlgItem( file_hWnd, FILE_LN_2 + GD_STATUS ),
                         " *Overdrive*  ");
                sb_move (file_hWnd, 2, 69);
                sb_puts( GetDlgItem( file_hWnd, FILE_LN_2 + GD_DTTM ), j );
                sb_show ();
                }
            else
                {
                gotoxy (locate_x, locate_y);
                (void) show_num (args, args->SendBLK - 1);
                (void) cputs (":");
                (void) show_num (args, args->ACKBLK);
                (void) cputs (" *Overdrive*  ");
                }
            }
        }
    else
        {
        if (fullscreen && un_attended)
            {
            elapse_time();

            sb_move (file_hWnd, 2, 2);
            TmpPtr = show_num (args, args->SendBLK - 1);
            sb_puts( GetDlgItem( file_hWnd, FILE_LN_2 + GD_TOTAL ),
                     TmpPtr );
#ifndef MILQ
            (void) sb_putc (file_hWnd, ':');
#endif
            TmpPtr = show_num (args, args->ACKBLK);
            sb_puts( GetDlgItem( file_hWnd, FILE_LN_2 + GD_SIZE ),
                     TmpPtr );

#ifndef MILQ
            sb_puts (file_hWnd, "              ");
#endif
            sb_move (file_hWnd, 2, 69);
            sb_puts( GetDlgItem( file_hWnd, FILE_LN_2 + GD_DTTM ), j );
            sb_show ();
            }
        else
            {
            gotoxy (locate_x, locate_y);
            (void) show_num (args, args->SendBLK - 1);
            (void) cputs (":");
            (void) show_num (args, args->ACKBLK);
            (void) cputs ("              ");
            }
        }
   happy_compiler = * (int *) TmpPtr; /* Makes the compiler happy! */
}

int ACInit (XMARGSP, int);
int ACEnd (XMARGSP, int);
int ACChkRcvd (XMARGSP);
int ACSLCheck (XMARGSP);
int ACSLVerify (XMARGSP);
int ACSLACKNAK (XMARGSP);
int ACXMCheck (XMARGSP);
int ACSLOCheck (XMARGSP);
int ACSL1Check (XMARGSP);
int ACACKNAK (XMARGSP);
int ACXMACK (XMARGSP);
int ACXMNAK (XMARGSP);
int ACRESYNC (XMARGSP);

STATES ACKNAK_Check[] = {
   { "ACInit", ACInit },
   { "ACEnd",  ACEnd },
   { "AC0",    ACChkRcvd },
   { "AC1",    ACSLCheck },
   { "AC2",    ACSLVerify },
   { "AC3",    ACSLACKNAK },
   { "AC4",    ACXMCheck },
   { "AC5",    ACSLOCheck },
   { "AC6",    ACSL1Check },
   { "AC7",    ACACKNAK },
   { "AC8",    ACXMACK },
   { "AC9",    ACXMNAK },
   { "AC10",   ACRESYNC }
};

int ACInit (XMARGSP args, int start_state)
{
    args->result = 0;
    return (start_state);
}

int ACEnd (XMARGSP args, int cur_state)
{
    args->result = cur_state;
    return (cur_state);
}

int ACChkRcvd (XMARGSP args)
{
    if (PEEKBYTE () >= 0)
        {
        args->CHR = TIMED_READ (0);
        return (AC1);
        }

    return (SUCCESS);
}

int ACSLCheck (XMARGSP args)
{
    if (args->ACKST > 2)
        return (AC2);

    return (AC6);
}

int ACSLVerify (XMARGSP args)
{
    if (args->ARBLK8 == (unsigned char) ((~args->CHR) & 0xff))
        {
        args->ARBLK = args->SendBLK - ((args->SendBLK - args->ARBLK8) & 0xff);
        return (AC3);
        }

    args->options.SEAlink = 0;
    args->Window = 1;
    args->ACKST = 0;
    return (AC6);
}

int ACSLACKNAK (XMARGSP args)
{
    if ((args->ARBLK < 0)
    ||  (args->ARBLK > args->SendBLK)
    ||  (args->ARBLK <= (args->SendBLK - 128)))
        {
        return (AC0);
        }

    if (args->ACKST == 3)
        {
        args->options.SEAlink = (~no_sealink) & 1;
        args->Window = calc_window ();
        args->ACKBLK = args->ARBLK;
        ++(args->ACKsRcvd);
        args->ACKST = 0;
        return (AC5);
        }

    args->SendBLK = args->ARBLK;
    args->curr_byte = (args->SendBLK - 1) * 128L;
    if (args->curr_byte < 0L)
        args->curr_byte = 0L;

    if (args->SendBLK > 0)
        ++(args->tot_errs);

    Get_Block (args);
    args->ACKST = 0;

    return (AC4);
}

int ACXMCheck (XMARGSP args)
{
    if (args->NumNAK < 4)
        {
        args->options.SEAlink = (~no_sealink) & 1;
        args->Window = calc_window ();
        }
    else
        {
        args->options.SEAlink = 0;
        args->Window = 1;
        }
    return (SUCCESS);
}

int ACSLOCheck (XMARGSP args)
{
    if ((args->options.SLO == 0) || (args->ACKsRcvd < 10))
        return (SUCCESS);

    args->options.SLO = 0;
    return (SUCCESS);
}

int ACSL1Check (XMARGSP args)
{
    if ((args->ACKST == 1) || (args->ACKST == 2))
        {
        args->ARBLK8 = (unsigned char) args->CHR;
        args->ACKST += 2;
        return (AC6);
        }

    if ((args->options.SEAlink == 0) || (args->ACKST == 0))
        return (AC7);

    return (AC0);
}

int ACACKNAK (XMARGSP args)
{
    long mac_timer;

    switch (args->CHR)
        {
        case ACK:
            args->ACKST = 1;
            args->NumNAK = 0;
            return (AC8);

        case WANTCRC:
            args->options.do_CRC = 1;
            /* Fallthrough */

        case NAK:
            args->ACKST = 2;
            ++(args->NumNAK);
            CLEAR_OUTBOUND ();
            timer (6);
            return (AC9);

        case SYN:
            CLEAR_OUTBOUND ();
            if (!no_resync)
                {
                args->result = Receive_Resync (&(args->resync_block));
                args->ACKST = 0;
                return (AC10);
                }
            else
                {
                return (AC0);
                }

        case DC3: /* ^S */
            if (args->options.SEAlink && (args->ACKST == 0))
                {
                mac_timer = timerset (1000);
                while (CARRIER && !timeup (mac_timer))
                    {
                    if (TIMED_READ (0) == DC1)
                        break;

                    time_release ();
                    }
                return (AC0);
                }

            /* Otherwise, fallthrough */

        default:
            break;
        }
   return (AC0);
}

int ACXMACK (XMARGSP args)
{
    if (!args->options.SEAlink)
        ++(args->ACKBLK);

    return (AC0);
}

int ACXMNAK (XMARGSP args)
{
    if (!args->options.SEAlink)
        {
        args->SendBLK = args->ACKBLK + 1;
        args->curr_byte = (args->SendBLK - 1) * 128L;
        if (args->curr_byte < 0L)
            args->curr_byte = 0L;

        if (args->SendBLK > 0)
            ++(args->tot_errs);

        Get_Block (args);
        }

    return (AC0);
}

int ACRESYNC (XMARGSP args)
{
    CLEAR_OUTBOUND ();
    if (args->result != SUCCESS)
        {
        SENDBYTE (NAK);
        return (SUCCESS);
        }

    if (args->SendBLK == 1)
        {
        args->prev_bytes = (args->resync_block - 1) * 128;
        if (args->prev_bytes > args->filelen)
            args->prev_bytes = args->filelen;
        status_line (MSG_TXT(M_SYNCHRONIZING), args->prev_bytes);
        }
    else
        {
        ++(args->tot_errs);
        }

    args->options.SEAlink = 1;
    args->Window = calc_window ();
    args->SendBLK = args->resync_block;
    args->curr_byte = (args->SendBLK - 1) * 128L;
    if (args->curr_byte < 0L)
        args->curr_byte = 0L;

    Get_Block (args);
    args->ACKBLK = args->SendBLK - 1;
    SENDBYTE (ACK);
    return (SUCCESS);
}


void Check_ACKNAK (XMARGSP args)
{
    (void) state_machine (ACKNAK_Check, args, AC0);
}

int Receive_Resync (long *resync_block)
{
    unsigned char resyncit[30];
    unsigned char *p;
    unsigned char a, b;
    unsigned int nak_crc, his_crc;

    p = resyncit;

    while ((*p = (unsigned char) TIMED_READ(1)) != ETX)
        {
        if ((*p < '0') || (*p > '9'))
            {
            status_line (">SEAlink Send: Resync bad byte '%02x'", *p);
            return (RESYNC_ERR);
            }
        ++p;
        }
    *p = '\0';
    nak_crc = crc_block ((unsigned char *) resyncit, (int) strlen ((char *) resyncit));
    a = (unsigned char) TIMED_READ (1);
    b = (unsigned char) TIMED_READ (1);
    his_crc = (b << 8) | a;

    if (nak_crc != his_crc)
        {
        status_line (">SEAlink Send: Resync bad crc %04x/%04x", nak_crc, his_crc);
        return (CRC_ERR);
        }

    *resync_block = atol ((char *) resyncit);

    status_line (">SEAlink Send: Resync to %ld", *resync_block);
    return (SUCCESS);
}

int calc_window ()
{
    int window;

    window = (int) (cur_baud.rate_value / 400);
    if (window <= 0)
        window = 2;
    if (small_window)
        window = (window > 6) ? 6 : window;

    return (window);
}