/**************************************************************************

    FMR version Makeing by ken 89.9.5

***************************************************************************/
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <fmc.h>

#define unlink	remove

typedef long TIME_T;

extern int port,bps;
extern void	Send_Failure();

TIME_T gtime(rt)
long   *rt;
{
    long      l;

    l = clock() / CLK_TCK;
    if ( rt != NULL )
	*rt = l;
    return l;
}
int    Async_BuffeR_Check( chx )
unsigned char *chx;
{
    int     data,len;
    unsigned int status;

    RSB_read(port,&len);
    if ( len != 0 ) {
	RSB_receive(port,&data,&status);
	*chx = data;
    }
    return len;
}
void   Async_Send(chx)
int    chx;
{
    unsigned int status;

    RSB_send(port,chx,&status);
}

#define STMsg        0
#define STFile       1
#define STUpDow      2
#define STType       3
#define STComSent    4
#define STDataSent   5
#define STErrSent    6
#define STPacSent    7
#define STComRate    8
#define STDataRate   9
#define STElapsed    10

#define STComRead    20
#define STDataRead   21
#define STErrRead    22
#define STPacRead    23
#define STUplSize    24
#define STDowSize    25
#define STUplRem     26
#define STDowRem     27
#define STRemTime    28

/***********************************************************************/
/****
    BPLUS.INC - B Plus Protocol Support routines
	  (derived from BPROTO.INC)

    Copyright 1987, CompuServe Incorporated

    These routines may be used as-is or in modified form in any
    personal computer terminal program to implement support of the
    CompuServe B and B Plus Protocols for the purpose of transfering
    information between CompuServe hosts and a personal computer.

    No warranty, expressed or implied, covers this code, or the specifications
    of the B and B Plus Protocols.


  Last update:
       Russ Ranshaw      16-Dec-87   Corrected Upload Abort problems.
       Russ Ranshaw      07-Apr-88   Corrected additional Abort problems.
       Russ Ranshaw      09-Apr-88   Added Quote Set to + Packet.
       Russ Ranshaw      10-Apr-88   Added Download Resume.
       Russ Ranshaw      22-Apr-88   Added File Information to Download.
                                     (File length only.)
       Russ Ranshaw      11-May-88   Added check to control Upload degradation
                                     under Send Ahead.
       Russ Ranshaw      16-May-88   Remove debugging code for release
                                     of Version 2.1
       Russ Ranshaw      01-Jun-88   Added externally settable file size for
                                     Downloads.  Use ST_Yes_or_No instead of
                                     ST_Prompt to get Y/N response.
       Russ Ranshaw      07-Jun-88   Added defensive check to see if Aborting
                                     is already true.  Changed comm. rate
                                     calculation.
       Russ Ranshaw      23-Jun-88   Add check for <DLE><B> in ReSync.
       Russ Ranshaw      04-Aug-88   Added WACK intercept to update the

                                     status display, mostly so that resumes
                                     show some activity while the host calculates
                                     it's CRC value.
	Paul Resch	 28-Aug-88   Converted to standard C code.
****/

/****************
**
**  This module implements the B-Protocol Functions.
**
**
**  If you have any questions, contact:
**      Russ Ranshaw, CompuServe Incorporated
**         [70000,1010]
**
**  This source was originally derived from BP.C, written by
**  Steve Wilhite, CompuServe Incorporated.
**
*****************/

/*
const
  UnitVersion = "2.2c";
  UnitVerDate = "04 Aug 88";
  UnitUpdBy   = "RWR";
*/

/****************************************************************************/
/*
 * C implementation notes:
 *	Routines with names beginning "ST_" are not supplied.  They are
 *		strictly console I/O.  Implement as needed, or see the PASCAL
 *		version.
 *	"Async_Send" is an extern that is passed a byte for output to the
 *		serial port.
 *	"Async_BuffeR_Read" is an extern that is passed the address of a byte.
 *		If TRUE is returned, the byte will be from the serial port.
 *		If FALSE is returned, the byte value is undefined.
 *	The CRC subroutines are included in this module.
 *
 *	I have tried to keep as close as possible to Mr. Ranshaw's structure.
 *	Please see the PASCAL source to clarify any confusion.
 */
/****************************************************************************/

#ifdef  DEBUG
#define	STATIC
#else
#define	STATIC	static
#endif

#define	TRUE	1
#define	FALSE	0

STATIC	TIME_T	e_timer;

typedef char  maxstr[256];

int     BP_Auto_Resume = FALSE;  /* True to automatically attempt transfer */
                                 /* resumption if the Initiator can do it  */
int	BP_Use_File_Size = FALSE;
long	BP_File_Size=0;
/* BP_Quote_This is invoked to set bits in BP_Special_Quote_Set. */
/* It must be called prior to calling BP_DLE_Seen for each character in the */
/* ranges 0x00 -> 0x1f and 0x80 -> 0x9f that is to be quoted. */
/*	BP_Quote_This (int Value); */

/* BP_Term_ENQ is invoked when Terminal Mode receives <ENQ> from host */
/*	BP_Term_ENQ(); */

/* BP_Term_ESC_I is invoked when Terminal Mode receives <ESC><I> from host */
/*	BP_Term_ESC_I (maxstr ESC_I_Response); */

/* BP_DLE_Seen is invoked when Terminal Mode receives <DLE> from host */
/*	BP_DLE_Seen(); */

/*===========================================================================*/

typedef unsigned char QS_Array[8];
typedef unsigned char QS_Array_p;

#define	bps300		0
#define	bps600		1
#define	bps1200		2
#define	bps2400		3
#define	bps4800		4
#define	bps9600		5
int	PortBps = bps2400;

STATIC	int	seq_num;       /* Current Sequence Number - init by Term_ENQ */
STATIC	unsigned short	checksum;          /* May hold CRC */

         /* Initiator's Parameters */
STATIC  unsigned char   His_WS,                      /* Initiator's Window Send     */
  His_WR,                      /* Initiator's Window Receive  */
  His_BS,                      /* Initiator's Block Size      */
  His_CM;               /* Initiator's Check Method    */
STATIC	QS_Array	His_QS;           /* Initiator's Quote Set */
         /* The next 3 Parameters are for the B Plus File Transfer Application */
STATIC	unsigned char His_DR=0, /* Initiator's Download Recovery Option */
  His_UR=0,                     /* Initiator's Upload Recovery Option */
  His_FI=0;               	/* Initiator's File Information Option */

         /* Negotiated Parameters */
STATIC	unsigned char  Our_WS=0,	/* Negotiated Window Send   */
  Our_WR=0,			/* Negotiated Window Receive */
  Our_BS=0,			/* Negotiated Block Size     */
  Our_CM=0;			/* Negotiated Check Method   */
STATIC	QS_Array	Our_QS;	/* Our Quote Set */
STATIC	unsigned char Our_DR=0,	/* Our Download Recovery Option */
  Our_UR=0,			/* Our Upload Recovery Option */
  Our_FI=0,			/* Our File Information Option */
  Def_DR=0,			/* User's preferred DOW Resume option */
  Def_BS=0;                       /* Default Block Size: varies depending */
					/* on the baud in use */
STATIC unsigned char Port_Update_Rate=0;
	/* Number of port bytes between Status */
                               /* upldates for the Port     */
STATIC	int	B_Plus=0;       /* True if B Plus in effect  */
STATIC	int	Use_CRC=0;       /* True if CRC in effect     */
STATIC	int	BP_Special_Quoting = 0;/* True to use BP_Special_Quote_Set */
STATIC	QS_Array BP_Special_Quote_Set =  /* User's specified Quote Set */
         {0x14, 0x00, 0xd4, 0x00,   /* ETX ENQ DLE XON XOFF NAK */
          0x00, 0x00, 0x00, 0x00
         };

STATIC	int	Buffer_Size=0;       /* Our_BS * 4                */
STATIC	int	SA_Max=0;   /* 1 if SA not enabled, else Max_SA */
STATIC	int	SA_Error_Count=0;   /* # of times S_Send_Data called */

STATIC	unsigned char	Quote_Table[256];   /* The quoting table */

STATIC	QS_Array  DQ_Full =
          {0xff, 0xff, 0xff, 0xff,
	   0xff, 0xff, 0xff, 0xff
          };
STATIC	QS_Array  DQ_Default =
        {0x14, 0x00, 0xd4, 0x00,    /* ETX ENQ DLE XON XOFF NAK */
         0x00, 0x00, 0x00, 0x00
        };
STATIC	QS_Array  DQ_Minimal =
        {0x14, 0x00, 0xd4, 0x00,    /* ETX ENQ DLE XON XOFF NAK */
         0x00, 0x00, 0x00, 0x00
        };
STATIC	QS_Array  DQ_Extended =
       {0x14, 0x00, 0xd4, 0x00,     /* ETX ENQ DLE XON XOFF NAK */
        0x00, 0x00, 0x50, 0x00      /* XON XOFF */
       };

#define   Max_Buf_Size   1032        /* Largest data block we can handle */
#define   Max_SA  2                  /* Maximum number of waiting Packets */

#define   Def_Buf_Size   511         /* Default data block               */
#define   Def_WS         1           /* I can send 2 Packets ahead       */
#define   Def_WR         1           /* I can receive single send-ahead  */
#define   Def_CM         1           /* I can handle CRC                 */
#define   Def_DQ         1           /* I can handle non-quoted NUL      */
                                       /* (including the `Tf' Packet       */
#define   Def_UR         0           /* I can NOT handle Upload Recovery */
#define   Def_FI         1           /* I can handle File Information */

#define   max_Errors     10


/* Receive States */

#define   R_Get_DLE       0
#define   R_Get_B         1
#define   R_Get_Seq       2
#define   R_Get_Data      3
#define   R_Get_Check     4
#define   R_Send_ACK      5
#define   R_Timed_Out     6
#define   R_Success       7

/* Send States */

#define   S_Get_DLE       1
#define   S_Get_Num       2
#define   S_Have_ACK      3
#define   S_Get_Packet    4
#define   S_Skip_Packet   5
#define   S_Timed_Out     6
#define   S_Send_NAK      7
#define   S_Send_ENQ      8
#define   S_Send_Data     9

/* Other Constants */

#define   dle   16
#define   etx   03
#define   nak   21
#define   enq   05

typedef char    lstr[256];
typedef struct {
	int	seq;    /* Packet's sequence number  */
	int	num;    /* Number of bytes in Packet */
	unsigned char	buf[Max_Buf_Size]; /* Actual Packet data        */
	} buf_type;

/****************************************************************************/
void    ltoa( n, s )
register long	n;
register char	*s;
{
	register char	*t;
	register long	i;
	register char	c;
	long	sign;
	long	divten;

	if( (sign = n) < 0 )
		n = -n;
	t = s;
	do
	{
		i = (divten = (n / 10)) * 10;	/* get ones digit (sigh) */

		*t++ = ( n - i ) + '0';
	} while( (n = divten) > 0 );
	if( sign < 0 )
		*t++ = '-';
	*t-- = '\0';
	while( s < t )
	{
		c = *t;
		*t-- = *s;
		*s++ = c;
	}
}
/****************************************************************************/
/*
  Clear_Quote_Table:
    Initialize Quote_Table to all zeros (nothing quoted).
*/

STATIC void Clear_Quote_Table()
{
	int	i;


	for( i = 0; i<=255; i++ )
		Quote_Table [i] = 0;
}

/*
  Update_Quote_Table:
    Sets the i-th entry of Quote_Table to the necessary quoting character
    according to the i-th bit of the supplied Quote Set.
*/

STATIC void Update_Quote_Table (Quote_Set)
QS_Array_p *Quote_Set;
{
	int	i, j, k;
	unsigned char	b, c;

	k = 0;
	c = 0x40;

	for( i = 0; i<=7; i++ )
	{
		if( i == 4 )
                    /* Switch to upper control set */
		{
			c = 0x60;
			k = 128;
		}

		b = Quote_Set [i];

		for (j = 0; j<=7; j++ )
		{
			if ((b & 0x80) != 0)
				Quote_Table [k] = c;

			b = b << 1;
			c = c + 1;
			k = k + 1;
		}
	}
}

/* BP_Quote_This sets bits in BP_Special_Quote_Set.                 */
/* It sets BP_Special_Quoting true to use the special quote set.    */
/* If Value = -1, the Special Quote Set is restored to its default. */

void    BP_Quote_This (Value)
int	Value;
{
	int	i, j;

	if ((Value >= 0x00 && Value <= 0x1F) ||
		(Value >= 0x80 && Value <= 0x9f) )
	{
		if (Value > 0x1f)
		{
			i = 4;
			Value = Value & 0x1f;
		}
		else
			i = 0;

		i = i + Value / 8;       /* = index into BP_Special_Quote_Set */
		j = Value % 8;           /* = Bit number in the i-th byte */
		BP_Special_Quote_Set [i] =
			BP_Special_Quote_Set [i] || (0x80 >> j);
		BP_Special_Quoting = TRUE;
	}
	else if (Value == -1)              /* Restore the Quote Set? */
	{
		for( i=0; i<sizeof(DQ_Minimal); i++ )
			BP_Special_Quote_Set[i] = DQ_Minimal[i];
		BP_Special_Quoting = FALSE;
	}
}

/*
  BP_Term_ENQ is called when the terminal emulator receives the character <ENQ>
  from the host.  Its purpose is to initialize for B Protocol and tell the
  host that we support B Plus.
*/


void    BP_Term_ENQ()
{
	int i;

	seq_num = 0;
	Buffer_Size = 512;               /* Set up defaults */
	Our_WS = 0;
	Our_WR = 0;
	Our_BS = 4;
	Our_CM = 0;
	Our_DR = 0;
	Our_UR = 0;
	Our_FI = 0;

	B_Plus      = FALSE;             /* Not B Plus Protocol */
	Use_CRC     = FALSE;             /* Not CRC_16      */
	SA_Max      = 1;                 /* Single Packet send */
	SA_Error_Count = 0;              /* No Upload errors yet */

  /* Set up Our prefered Quoting Mask */
	for( i=0; i<sizeof(DQ_Minimal); i++ )
		Our_QS[i] = DQ_Minimal[i];

	Clear_Quote_Table();
	Update_Quote_Table (Our_QS);

	Async_Send (dle);
	Async_Send ('+');
	Async_Send ('+');
	Async_Send (dle);
	Async_Send ('0');
}

/*
  BP_Term_ESC_I is called when <ESC><I> is received by the terminal emulator.
  Note that CompuServe now recognizes the string ",+xxxx" as the final field.
  THis provides a checksum (xxxx being the ASCII decimal representation of the
  sum of all characters in the response string from # to +.  The purpose of
  the checksum is to eliminate the need for retransmission and comparison of
  the response.
*/


void    BP_Term_ESC_I (esc_I_Response)
char	esc_I_Response[];
{
	int	i;
	maxstr	t;
	int	cks;	/* checksum */

	cks = 0;

	for (i = 0; i<strlen (esc_I_Response); i++ )
	{
		Async_Send (esc_I_Response [i]);
		cks += esc_I_Response [i];
	}

	Async_Send (',');
	Async_Send ('+');
	cks = cks + ',' + '+';

	ltoa( (long)cks, t );

	i = 4 - strlen( t );	/* zero-fill */
	while( i-- )
		Async_Send( '0' );

	for (i = 0; i<strlen (t); i++ )
		Async_Send (t [i]);

	Async_Send (0x0d);    /* <CR> */
}


/*
  BP_DLE_Seen is called from the main program when the character <DLE> is
  received from the host.

  This routine calls Read_Packet and dispatches to the appropriate
  handler for the incoming Packet.
*/


STATIC	int    ttime=0,
	R_Size=0,           /* size of receiver buffer */
	ch=0;               /* current character */

STATIC	int    xoff_flag=0,
    Timed_Out=0,            /* we timed out before receiving character */
    Packet_Received=0,      /* True if a Packet was received */
    masked=0;               /* true if ctrl character was quoted */

STATIC	buf_type	SA_Buf[Max_SA+1];  /* Send-ahead buffers */

STATIC	int    SA_Next_to_ACK=0;    /* Which SA_Buf is waiting for an ACK */
STATIC	int    SA_Next_to_Fill=0;   /* Which SA_Buf is ready for new data */
STATIC	int    SA_Waiting=0;        /* Number of SA_Buf's waiting for ACK */
STATIC	int    Aborting=0;          /* True if aborting the transfer ]*/

STATIC	unsigned char    R_buffer[Max_Buf_Size];
STATIC	lstr    filename=0;                   /* pathname */
STATIC	int    	S_Counter=0;                  /* Used to pace status update */
STATIC	int    	R_Counter=0;
STATIC	long    S_Com_Data=0;                 /* Comm Port Data traffic */
STATIC	long    R_Com_Data=0;
STATIC	long    S_File_Data=0;                /* File Data Traffic */
STATIC  long    R_File_Data=0;
STATIC	long    S_Packet_Count=0;             /* Packet count */
STATIC	long    R_Packet_Count=0;
STATIC	long    S_Error_Count=0;               /* Error count */
STATIC	long    R_Error_Count=0;
STATIC	long    S_File_Size=0;         /* Length of file already sent */
STATIC	long    R_File_Size=0;         /* Length of file already received */
STATIC	long    S_Remaining=0;         /* # bytes remaining to be sent */
STATIC	long    R_Remaining=0;         /* # bytes reamining to be received */
STATIC	long    Com_Rate=0;            /* Comm. bytes per second */
STATIC	long    Data_Rate=0;           /* Effective Data bytes per second */
STATIC	long    Time_Estimate=0;       /* Estimated time until completion */
STATIC	int	Resume_Flag=0;         /* TRUE if attempting a DOW resume */

/***************************************************************************/
/*
 * crc
 *
 * Calculates XMODEM-style CRC (uses the CCITT V.41 polynomial but
 * completely backwards from the normal bit ordering).
 */


STATIC	unsigned	crc_table[] = {
        0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
        0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
        0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
        0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
        0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
        0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
        0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
        0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,

        0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
        0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
        0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
        0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
        0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
	0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
        0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
        0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
        0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
        0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
        0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
        0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
        0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
        0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
        0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
        0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
        0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
        0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
        0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
        0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
        0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
        0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
        0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
        0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
	};

STATIC	unsigned int	crc_16;

/*
 * Init_CRC initializes for XMODEM style CRC calculation by setting
 * crc_16 to value.  Typically value is 0 for XMODEM and -1 for
 * B+ Protocl.  It returns the initial value.
 */
STATIC	int	Init_CRC (value)
int	value;
{
	return( crc_16 = value );
}

/*
  Upd_CRC updates crc_16 and returns the updated value. */

STATIC	unsigned int	Upd_CRC (value)
unsigned int	value;
{
	crc_16 = crc_table [((crc_16 >> 8) ^ (value)) & 0xff] ^ (crc_16 << 8);
	return( crc_16 );
}
/***************************************************************************/

STATIC	void do_checksum (c)
int	c;
{
	if (B_Plus && Use_CRC)
		checksum = Upd_CRC ((unsigned short)c);
	else
	{
		checksum = checksum << 1;

		if (checksum > 255)
			checksum = (checksum & 0xff) + 1;

		checksum += c;

		if (checksum > 255)
			checksum = (checksum & 0xff) + 1;
	}
}

STATIC	void send_byte (ch)
unsigned char	ch;
{
	Async_Send (ch);
	S_Com_Data++;
	S_Counter++;
	S_Counter = S_Counter % Port_Update_Rate;

	if (S_Counter == 0)
		ST_Display_Value (STComSent, S_Com_Data);
}

STATIC void send_masked_byte (ch)
int	ch;
{
	ch &= 0xff;

	if (Quote_Table [ch] != 0)
	{
		send_byte (dle);
		send_byte (Quote_Table [ch]);
	}
	else
		send_byte (ch);
}

STATIC void Send_ACK()
{
	send_byte (dle);
	send_byte (seq_num + '0');
}

STATIC void Send_NAK()
{
	send_byte (nak);
}

/***********************
STATIC void Send_ENQ()
{
	send_byte (enq);
}
***********************/

STATIC int read_byte()
{
	unsigned char	chx;
	unsigned short	Hiber;
	TIME_T	start;

	Timed_Out = FALSE;
	start = gtime(NULL);

	if ( !Aborting )
		Hiber = 30;
	else
		Hiber = 10;

	while ( !Async_BuffeR_Check( &chx ) )
	{

		if (gtime(NULL) - start >= Hiber)
		{
			Timed_Out = TRUE;
			return( FALSE );
		}
	}

	ch = chx;
	R_Com_Data++;
	R_Counter++;
	R_Counter = R_Counter % Port_Update_Rate;

	if (R_Counter == 0 )
		ST_Display_Value (STComRead, R_Com_Data);

	return(TRUE);
}


STATIC int read_masked_byte()
{
	masked = FALSE;

	if (read_byte() == FALSE)
		return(FALSE);

	if (ch == dle)
	{
		if (read_byte() == FALSE)
			return(FALSE);

		if (ch < 0x60)
			ch = ch & 0x1f;
		else
			ch = (ch & 0x1f) | 0x80;

		masked = TRUE;
	}

	return(TRUE);
}

/*
  Increment Sequence Number
*/

STATIC	int	Incr_Seq (value)
int	value;
{
	if (value == 9)
		return( 0);
	else
		return(value + 1);
}


STATIC	int	Read_Packet (Lead_in_Seen, From_Send_Packet)
int	Lead_in_Seen;
int	From_Send_Packet;
/*   Lead_in_Seen is true if the <DLE><B> has been seen already.  */


/*   From_Send_Packet is true if called from Send_Packet          */
/*        (causes exit on first error detected)                   */

/*   Returns True if Packet is available from host. */

{
	short  State,
		next_seq,
		block_num,
		errors,
		new_cks;
	int	i;
	int	NAK_Sent;

	if (Packet_Received)      /* See if a Packet was picked up on a call to */
                      /* Get_ACK */
	{
		Packet_Received = FALSE;
		return( TRUE);
	}

	NAK_Sent = FALSE;

	for( i=0; i<Buffer_Size; i++ )
		R_buffer[i] = 0;

	next_seq = (seq_num +  1) % 10;
	errors = 0;

	if (Lead_in_Seen)           /* Start off on the correct foot */
		State = R_Get_Seq;
	else State = R_Get_DLE;

	while( TRUE )
	{
		ttime = 300;

		switch  (State) {

		case R_Get_DLE :
			if (ST_Check_Abort() && !Aborting)
			{
				ST_Display_String (STMsg, "Aborting download per your request");
				Send_Failure ("AAborted by user");
				return(FALSE);
			}

			if ( !read_byte() )
				State = R_Timed_Out;
			else if ((ch & 0x7F) == dle )
				State = R_Get_B;
			else if ((ch & 0x7F) == enq )
				State = R_Send_ACK;
			break;

		case R_Get_B :
			if (!read_byte())
				State = R_Timed_Out;
			else if ((ch & 0x7F) == 'B')
				State = R_Get_Seq;
			else if (ch == enq)
				State = R_Send_ACK;
			else if (ch == ';')
			{
				ST_Display_Value (STComRead, R_Com_Data);
				State = R_Get_DLE;
			}
			else State = R_Get_DLE;
			break;

		case R_Get_Seq :
			if( Resume_Flag )   /* Improve status display for DOW resume */
			{
				e_timer = gtime(NULL);
				R_Com_Data = 2;
			}
			if (!read_byte())
				State = R_Timed_Out;
			else if (ch == enq)
				State = R_Send_ACK;
			else
			{
				if (B_Plus && Use_CRC)
					checksum = Init_CRC (0xffff);
				else checksum = 0;

				block_num = ch - '0';

				do_checksum (ch);

				i = 0;
				State = R_Get_Data;
			}
			break;

		case R_Get_Data :
			if (!read_masked_byte())
				State = R_Timed_Out;
			else if ((ch == etx) && !masked)
			{

				do_checksum (etx);
				State = R_Get_Check;
			}
			else
			{
				R_buffer[i] = ch;
				i = i + 1;
				do_checksum (ch);
			}
			break;

		case R_Get_Check :
			if (!read_masked_byte())
				State = R_Timed_Out;
			else
			{
				if (B_Plus && Use_CRC)
				{
					checksum = Upd_CRC ((unsigned int)ch);

					if (!read_masked_byte())
						new_cks = checksum ^ 0xff;
					else
					{
						checksum =
						    Upd_CRC((unsigned int)ch);
						new_cks = 0;
					}
				}
				else new_cks = ch;

				if (new_cks != checksum)
					State = R_Timed_Out;
				else if (R_buffer[0] == 'F') /* Watch for Failure Packet */
					State = R_Success; /* which is accepted regardless */
				else if (block_num == seq_num) /* Watch for duplicate block */
					State = R_Send_ACK; /* Simply ACK it */
				else if (block_num != next_seq)
					State = R_Timed_Out; /* Bad seq num */
				else State = R_Success;
			}
			break;

		case R_Timed_Out :
			errors++;

			if ((errors > max_Errors) || (From_Send_Packet))
				return( FALSE );

			if (!NAK_Sent || !B_Plus)
			{
				R_Error_Count++;
				ST_Display_Value (STErrRead, R_Error_Count);
				NAK_Sent = TRUE;
				Send_NAK();
			}

			State = R_Get_DLE;
			break;

		case R_Send_ACK :

			if (!Aborting)
				Send_ACK();

			State = R_Get_DLE;        /* wait for the next block */
			break;

		case R_Success :
			ST_Display_Value (STComRead, R_Com_Data);
			ST_Display_Value (STComSent, S_Com_Data);

			if (!Aborting)
				seq_num = block_num;

			R_Size = i;
			R_Packet_Count++;
			ST_Display_Value (STPacRead, R_Packet_Count);
			return(TRUE);

		}
	}

} /* Read_Packet */

STATIC	void Send_Data (BuffeR_Number)
int	BuffeR_Number;
{
	int	i;
	buf_type	*p;

	p = &SA_Buf [BuffeR_Number];

	if (B_Plus && Use_CRC)
		checksum = Init_CRC (0xffff);
	else checksum = 0;

	send_byte (dle);
	send_byte ('B');

	send_byte (p->seq + '0');
	do_checksum (p->seq + '0');

	for (i = 0; i<=p->num; i++ )
	{
		send_masked_byte (p->buf [i]);
		do_checksum (p->buf[i]);
	}

	send_byte (etx);
	do_checksum (etx);

 	if (B_Plus && Use_CRC)
		send_masked_byte (checksum >> 8);

	send_masked_byte (checksum);

}

STATIC	int	Incr_SA (Old_Value)
int	Old_Value;
{
	if (Old_Value == Max_SA)
		return(0);
	else
		return(Old_Value + 1);
}

/*
  ReSync is called to restablish syncronism with the remote.  This is
  accomplished by sending <ENQ><ENQ> and waiting for the sequence
  <DLE><d><DLE><d> to be received, ignoring everything else.

  Return is -1 on time out, `B` if <DLE><B> seen, else the digit <d>.
*/

STATIC	int	ReSync()
#define	  Get_First_DLE     1
#define	  Get_First_Digit   2
#define	  Get_Second_DLE    3
#define	  Get_Second_Digit  4
{
	int	State,
	Digit_1;

	send_byte (enq);     /* Send <ENQ><ENQ> */
	send_byte (enq);
	State = Get_First_DLE;

	while(1)
	{
		switch (State) {
		case Get_First_DLE :
			if (!read_byte())
				return(-1);

			if (ch == dle)
				State = Get_First_Digit;
			break;

		case Get_First_Digit :
			if (!read_byte())
				return(-1);

			if ((ch >= '0') && (ch <= '9'))
			{
				Digit_1 = ch;
				State = Get_Second_DLE;
			}
			else if (ch == 'B')
				return( ch );
			break;

		case Get_Second_DLE :
			if (!read_byte())
				return(-1);

			if (ch == dle)
				State = Get_Second_Digit;
			break;

		case Get_Second_Digit :
			if (!read_byte())
				return(-1);

			if ((ch >= '0') && (ch <= '9'))
			{
				if (Digit_1 == ch  )
					return(ch);
				else if (ch == 'B')
					return( ch );
				else
				{
					Digit_1 = ch;
					State = Get_Second_DLE;
				}
			}
			else State = Get_Second_DLE;
			break;

		} /* case */
	}  /* while TRUE */
}

/*
  Get_ACK is called to wait until the SA_Buf indicated by SA_Next_to_ACK
  has been ACKed by the host.
*/

STATIC	int	Get_ACK()
{
	int	State,
		errors,
		block_num,
		i;
/*  int	new_cks;*/
	int	Sent_ENQ;
	int	SA_Index;

	Packet_Received = FALSE;
	errors = 0;
	Sent_ENQ = FALSE;
	State = S_Get_DLE;

	while(1)
	{
	switch (State) {
	case S_Get_DLE :
		ttime = 300;

		if (ST_Check_Abort() && !Aborting)
		{
			ST_Display_String (STMsg,
				"Aborting the upload per your request");
			Send_Failure ("AAborted by user");
			return(FALSE);
		}

		if (!read_byte()  )
			State = S_Timed_Out;
		else
		{
			if (ch == dle)
				State = S_Get_Num;
			else if (ch == nak )
				State = S_Send_ENQ;
			else if (ch == etx )
				State = S_Send_NAK;
		}
		break;

	case S_Get_Num :
		if (!read_byte() )
			State = S_Timed_Out;
		else if ((ch >= '0') && (ch <= '9'))
			State = S_Have_ACK;           /* Received ACK */
		else if (ch == 'B' )
		{
			if (!Aborting)
				State = S_Get_Packet; /* Try to receive a Packet */
			else State = S_Skip_Packet;   /* Try to skip a Packet */
		}
		else if (ch == nak)
			State = S_Send_ENQ;
		else if (ch == ';')
		{	/* Received a WACK (Wait Acknowledge) */
			ST_Display_Value (STComRead, R_Com_Data);
			State = S_Get_DLE;
		}
		else State = S_Timed_Out;
		break;

	case S_Get_Packet :
		if (Read_Packet (TRUE, TRUE) )
		{
			Packet_Received = TRUE;

			if (R_buffer [0] == 'F')  /* Check for Failure Packet */
			{
				Send_ACK();
				return(FALSE);
			}

			State = S_Get_DLE;     /* Stay here to find the ACK */
		}
		else State = S_Get_DLE;         /* Receive failed; keep watching for ACK */
		break;

	case S_Skip_Packet :    /* Skip an incoming Packet */
		if (!read_byte())
			State = S_Timed_Out;
		else if (ch == etx )
		{                  /* Get the Checksum or CRC */
			if (!read_masked_byte())
				State = S_Timed_Out;
			else if (!Use_CRC)
				State = S_Get_DLE;
			else if (!read_masked_byte())
				State = S_Timed_Out;
			else State = S_Get_DLE;
		}
		break;

	case S_Have_ACK :
		block_num = ch - '0';
		ST_Display_Value (STComSent, S_Com_Data);
		ST_Display_Value (STComRead, R_Com_Data);

		if (SA_Buf [SA_Next_to_ACK].seq == block_num  )
		{                  /* This is the one we're waiting for */
			SA_Next_to_ACK = Incr_SA (SA_Next_to_ACK);
			SA_Waiting = SA_Waiting - 1;

			if (SA_Error_Count > 0 )     /* Apply heuristic to control */
			SA_Error_Count--; /* Upload Performance degradation */

			return(TRUE);
		}
		else if ((SA_Buf [Incr_SA (SA_Next_to_ACK)].seq == block_num) &&
			SA_Waiting == 2)
		{                 /* Must have missed an ACK */
			SA_Next_to_ACK = Incr_SA (SA_Next_to_ACK);
			SA_Next_to_ACK = Incr_SA (SA_Next_to_ACK);
			SA_Waiting = SA_Waiting - 2;

			if (SA_Error_Count > 0)
				SA_Error_Count--;

			return(TRUE);
		}
		else if (SA_Buf [SA_Next_to_ACK].seq == Incr_Seq (block_num) )
		{
			if (Sent_ENQ)
				State = S_Send_Data; /* Remote missed first block */
			else State = S_Get_DLE;       /* Duplicate ACK */
		}
		else
		{
			if (!Aborting)        /* While aborting, ignore any */
				State = S_Timed_Out; /* ACKs that have been sent   */
			else State = S_Get_DLE;       /* which are not for the failure */
		}   		                        /* Packet. */

		Sent_ENQ = FALSE;
		break;

	case S_Timed_Out :
		State = S_Send_ENQ;
		break;

	case S_Send_NAK :
		errors++;
		S_Error_Count++;
		ST_Display_Value (STErrSent, S_Error_Count);

		if (errors > max_Errors)
			return(FALSE);

		Send_NAK();

		State = S_Get_DLE;
		break;

	case S_Send_ENQ :
		errors++;
		S_Error_Count++;
		ST_Display_Value (STErrSent, S_Error_Count);

		if ((errors > max_Errors) || (Aborting && (errors > 3)))
			return(FALSE);

		ch = ReSync();
		if (ch == -1)
			State = S_Get_DLE;
		else if (ch == 'B')
		{
			if( !Aborting )
				State = S_Get_Packet;	/* Try to receive a Packet */
			else
				State = S_Skip_Packet;	/* Try to skip a Packet */
		}
		else State = S_Have_ACK;
		Sent_ENQ   = TRUE;
		break;

	case S_Send_Data :
		SA_Error_Count += 3;

		if (SA_Error_Count >= 12) /* Stop Upload Send Ahead if too many */
		SA_Max = 1;           /* errors have occured */

		SA_Index = SA_Next_to_ACK;

		for (i = 1; i<=SA_Waiting; i++ )
		{
			Send_Data (SA_Index);
			SA_Index = Incr_SA (SA_Index);
		}

		State = S_Get_DLE;
		Sent_ENQ = FALSE;
		break;
	}
	}
} /* Get_ACK */

STATIC	int	send_Packet (size)
int	size;
{
	while (SA_Waiting >= SA_Max) /* Allow for possible drop out of Send Ahead */
	{
		if (!Get_ACK() )
			return(FALSE);
	}

	seq_num = Incr_Seq (seq_num);
	SA_Buf [SA_Next_to_Fill].seq = seq_num;
	SA_Buf [SA_Next_to_Fill].num = size;
	Send_Data (SA_Next_to_Fill);
	SA_Next_to_Fill = Incr_SA (SA_Next_to_Fill);
	SA_Waiting = SA_Waiting + 1;
	S_Packet_Count++;
	ST_Display_Value (STComSent, S_Com_Data);
	ST_Display_Value (STPacSent, S_Packet_Count);
	return(TRUE);
}

/*
  SA_Flush is called after sending the last Packet to get host's
  ACKs on outstanding Packets.
*/

STATIC	int	SA_Flush()
{
	while (SA_Waiting > 0)
	{
		if (!Get_ACK())
			return(FALSE);
	}
	return( TRUE );
}

STATIC	void Send_Failure (Reason)
char	Reason[];
{
	int	i;
	buf_type	*p;

	SA_Next_to_ACK = 0;
	SA_Next_to_Fill = 0;
	SA_Waiting = 0;
	Aborting   = TRUE;          /* Inform Get_ACK we're aborting ]*/

	p = &SA_Buf [0];
	p->buf [0] = 'F';
	for (i = 1; i<=strlen(Reason); i++ )
		p->buf [i] = (Reason [i]);

	if ( send_Packet (strlen(Reason)) )
		SA_Flush();   /* Gotta wait for the Initiator to ACK it */
}

/* Send_File is called to send a file to the host */

STATIC	int	Send_File (name)
char	name[];
{
	int	n;
  	FILE	*data_File;
	buf_type	*p;

	data_File = fopen (name,"rb");

	if (data_File == 0 )
	{
		ST_Display_String (STMsg, "Cannot find that file");
		Send_Failure ("MFile not found");
		return(FALSE);
	}

	fseek(data_File,0L,2);		/* seek to end of file */
	S_Remaining = ftell(data_File);	/* how long is this file ? */
	fseek(data_File,0L,0);		/* back to the start, ready to go */
	ST_Display_Value (STUplRem, S_Remaining);
/* Send_File_Information here ? */

/*----------------
	S_Com_Data = 0;
	R_Com_Data = 0;
	e_timer = gtime(NULL);
-------------------*/
	do
	{
		p = &SA_Buf [SA_Next_to_Fill];
	        p->buf [0] = 'N';
        	n = fread (&p->buf[1], 1, Buffer_Size, data_File);

		if (n > 0)
		{
			if (send_Packet (n) == FALSE)
			{
				fclose(data_File);
				return(FALSE);
			}

			S_File_Data = S_File_Data +  (n);
			S_File_Size = S_File_Size +  (n);
			S_Remaining = S_Remaining -  (n);
			ST_Display_Value (STUplSize, S_File_Size);
			ST_Display_Value (STDataSent, S_File_Data);
			ST_Display_Value (STUplRem, S_Remaining);
			Time_Estimate = gtime(NULL) - e_timer;
			ST_Display_Value (STElapsed, Time_Estimate);

			if (Time_Estimate != 0)
			{
				Com_Rate = S_Com_Data / Time_Estimate;
				Data_Rate = S_File_Data / Time_Estimate;
				ST_Display_Value (STComRate, Com_Rate);
				ST_Display_Value (STDataRate, Data_Rate);

				if (Data_Rate != 0)
				{
					Time_Estimate = S_Remaining / Data_Rate;
					ST_Display_Value (STRemTime, Time_Estimate);
				}
			}
		}
	} while(n > 0);

	if (ferror(data_File) != 0)
	{
		Send_Failure ("EFile read failure");
		ST_Display_String (STMsg, "Read failure...aborting");
		fclose(data_File);
		return(FALSE);
	}

/* Inform host that the file was sent */

	p = &SA_Buf [SA_Next_to_Fill];
	p->buf [0] = 'T';
	p->buf [1] = 'C';

	if (send_Packet (2) == FALSE)
	{
		fclose (data_File);
		return(FALSE);
	}
	else
	{
		fclose (data_File);
		if (!SA_Flush())
			return(FALSE);
		return(TRUE);
	}

} /* Send_File */

/*
    Do_Transport_Parameters is called when a Packet type of + is received.
    It sends a Packet of Our local B Plus parameters and sets the Our_xx
    parameters to the minimum of the Initiator's and Our own parameters.
*/

STATIC	void Do_Transport_Parameters()
{
	int	Quote_Set_Present;
	int	i;
	buf_type	*p;

	if (BP_Special_Quoting)
	{
		for( i=0; i<8; i++ )
			Our_QS[i] = BP_Special_Quote_Set[i];
	}
	else
	{
		for( i=0; i<8; i++ )
			Our_QS[i] = DQ_Minimal[i];
	}

	for (i = R_Size + 1; i<=512; i++ ) R_buffer [i] = 0;

	His_WS = R_buffer [1];     /* Pick out Initiator's parameters */
	His_WR = R_buffer [2];
	His_BS = R_buffer [3];
	His_CM = R_buffer [4];

	His_QS [0] = R_buffer [7];
	His_QS [1] = R_buffer [8];
	His_QS [2] = R_buffer [9];
	His_QS [3] = R_buffer [10];
	His_QS [4] = R_buffer [11];
	His_QS [5] = R_buffer [12];
	His_QS [6] = R_buffer [13];
	His_QS [7] = R_buffer [14];

	His_DR = R_buffer [15];
	His_UR = R_buffer [16];
	His_FI = R_buffer [17];

	if (R_Size >= 14)
		Quote_Set_Present = TRUE;
	else Quote_Set_Present = FALSE;

	p = &SA_Buf [SA_Next_to_Fill];
	p->buf [0] = '+';  /* Prepare to return Our own parameters */
	p->buf [1] = Def_WS;
	p->buf [2] = Def_WR;
	p->buf [3] = Def_BS;
	p->buf [4] = Def_CM;
	p->buf [5] = Def_DQ;
	p->buf [6] = 0;          /* No transport layer here */

	for (i = 0; i<=7; i++ )
		p->buf [i + 7] = Our_QS [i];

	if (BP_Auto_Resume)      /* Set Download Resume according to */
		Def_DR = 2;       /* user's preference */
	else Def_DR = 1;

	p->buf [15] = Def_DR;
	p->buf [16] = Def_UR;
	p->buf [17] = Def_FI;

	Update_Quote_Table (DQ_Full);   /* Send the + Packet w/ full quoting */

	if (!send_Packet (17) )
		return;

	if (SA_Flush())                 /* Wait for host's ACK on Our Packet */
	{
		if (His_WS < Def_WR)      /* Take minimal subset of Transport Params. */
			Our_WR = His_WS;   /* If he can send ahead, we can receive it. */
		else Our_WR = Def_WR;

		if (His_WR < Def_WS)      /* If he can receive send ahead, we can send it. */
			Our_WS = His_WR;
		else Our_WS = Def_WS;


		if( His_BS < Def_BS)
			Our_BS = His_BS;
		else Our_BS = Def_BS;

		if (His_CM < Def_CM)
			Our_CM = His_CM;
		else Our_CM = Def_CM;

		if (His_DR < Def_DR)
			Our_DR = His_DR;
		else Our_DR = Def_DR;

		if (His_UR < Def_UR)
			Our_UR = His_UR;
		else Our_UR = Def_UR;

		if (His_FI < Def_FI)
			Our_FI = His_FI;
		else Our_FI = Def_FI;

		if (Our_BS == 0)
			Our_BS = 4;    /* Default */

		Buffer_Size = Our_BS * 128;

		B_Plus = TRUE;

		if (Our_CM == 1)
			Use_CRC = TRUE;

		if (Our_WS != 0)
			SA_Max = Max_SA;
	}

	Clear_Quote_Table();            /* Restore Our Quoting Set */
	Update_Quote_Table (Our_QS);

	if (Quote_Set_Present)     /* Insert Initiator's Quote Set */
		Update_Quote_Table (His_QS);
}

/* Check_Keep is called from Receive_File when a fatal error */
/* occurs.  It asks the user if the file should be retained */

STATIC	void Check_Keep (data_File, Name)
FILE	*data_File;
char	Name[];
{
	char	yn;
	char	str[80];

	fclose (data_File);

	if ((!BP_Auto_Resume) || (!B_Plus) || (Our_DR == 0))
	{
		strcpy( str, "Do you wish to retain the partial " );
		strcat( str, Name );
		strcat( str, "?" );
		ST_Yes_or_No( str, &yn );
	}
	else
		yn = 'Y';

	if (yn == 'N')
	{
		unlink ( Name );
		ST_Display_String (STMsg, "File erased.");
	}
	else
	{
/* implementation dependent option: Hide the file from casual view */
		ST_Display_String (STMsg, "File retained.");
	}
}

/* Process_File_Information is called from Receive_File when a TI Packet */
/* is received.  It extracts the desired information from the Packet.    */

STATIC	char	Val_Str[50];
STATIC	int	e_i=0, e_j=0, e_n=0;

STATIC void Extract_String()   /* Extract next string of characters */
{
	int	Digit_Seen;

	Digit_Seen = FALSE;
	e_j = 0;
	while (e_i <= e_n)
	{
		if ((R_buffer [e_i] >= '0') && (R_buffer [e_i] <= '9') )
		{
			Digit_Seen = TRUE;
			e_j++;
			Val_Str [e_j] = R_buffer [e_i];
		}
		else if (Digit_Seen)
		{
			Val_Str [0] = e_j;
			return;
		}

		e_i++;
	}
}

STATIC	void Process_File_Information()
{
	int	i;

	e_n = R_Size - 1;
	e_i = 4;       /* Skip data type and compression flag */
	Extract_String();
/*	Val (Val_Str, R_Remaining, e_j); */
	R_Remaining = 0;
	for( i=1; i<=e_j; i++ )
		R_Remaining = (R_Remaining * 10) + (Val_Str[i] - '0');
	R_Remaining = R_Remaining - R_File_Size;  /* Adjust for Dow Resume */
	ST_Display_Value (STDowRem, R_Remaining);

  /* Ignore rest of parameters for now */

	S_Packet_Count = 0;
	R_Packet_Count = 0;
}

/* Receive_File is called to receive a file from the host */

STATIC	int	Receive_File (Name)
char	Name[];
{
	FILE	*Data_File;
	int	status;
	long	File_Length;      /* For download resumption */
	lstr	Work_String;
	int	Packet_Len;
	int	i, n;
	char	yn;
	char	Dow_Type;
	buf_type	*p;

	Dow_Type = 'D';         /* Assume normal downloading */

	Data_File = fopen( Name, "rwb" );	/* open for r/w first */

	if (Data_File != NULL)	/* this file already exists */
	{                   /* See if we can try automatic resume */
		if ((Our_DR > 1) && BP_Auto_Resume)
			Dow_Type = 'R';  /* Remote supports `Tf', let's try */
		else if ((Our_DR > 0))
		{
			ST_Display_String (STMsg, "File already exists.");
			ST_Yes_or_No ("Do you wish to resume downloading? ", &yn);

			if (yn == 'Y')
				Dow_Type = 'R';
			else
				ST_Display_String (STMsg, "File being overwritten.");
		}
	}

	switch( Dow_Type ) {
	case 'D':
		if( Data_File )
			fclose( Data_File );	/* close the read/write file */
		Data_File = fopen( Name, "wb" );	/* open for write */
		if (Data_File == NULL)
		{
			Send_Failure ("CCannot create file");
			return(FALSE);
		}
		Send_ACK();
		break;

	case 'R' :
                     /* Resume download */
		/* file is open and at start */
		ST_Display_String (STMsg, "Calculating CRC");

		p = &SA_Buf [SA_Next_to_Fill];
		if (Dow_Type == 'R')
		{
			checksum = Init_CRC (0xffff);
			do {
				n = fread (&p->buf [0], 1, Buffer_Size,
					Data_File);
				for (i = 0; i<n; i++ )
					checksum =
					    Upd_CRC((unsigned int)p->buf [i]);
                	} while( n > 0 );
		}
		else
			checksum = 0;

		p->buf [0] = 'T';
		p->buf [1] = 'r';

		Packet_Len = 2;
		File_Length = ftell(Data_File);

		ltoa (File_Length, Work_String);
		strcat( Work_String, " " );

		for (i = 0; i<strlen(Work_String); i++ )
		{
			p->buf [Packet_Len] = Work_String [i];
			Packet_Len++;
		}

		ltoa ((long)checksum, Work_String);
		strcat( Work_String, " " );

		for (i = 0; i<strlen(Work_String); i++ )
		{
			p->buf [Packet_Len] = Work_String [i];
			Packet_Len++;
		}

		if (!send_Packet (Packet_Len - 1)) /* Send_Data sends 0..Size */
		{
			fclose (Data_File);
			return(FALSE);
		}

		if (!SA_Flush())
		{
			fclose (Data_File);
			return(FALSE);
		}

		R_File_Size = File_Length;
		ST_Display_Value (STDowSize, R_File_Size);
		ST_Display_String (STMsg, "Host calculating CRC...");
		Resume_Flag = TRUE;
		break;
	}


/*
  Process each incoming Packet until 'TC' Packet received or failure
*/

	R_Packet_Count = 0;
	S_Packet_Count = 0;

	if( BP_Use_File_Size )
		R_Remaining = BP_File_Size;
	else
		R_Remaining = 0;
  while(TRUE)
    {
      if (Read_Packet (FALSE, FALSE))
          {
            switch (R_buffer[0]) {
              case 'N' :
		if( Resume_Flag )
		{
			ST_Display_String( STMsg, "Resuming Download" );
			Resume_Flag = FALSE;
		}

		status = fwrite( &R_buffer[1], 1, R_Size - 1, Data_File );

                  if ((status != (R_Size - 1)))
                    {
                      ST_Display_String (STMsg, "Write failure...aborting");
                      Send_Failure ("EWrite failure");
                      Check_Keep (Data_File, Name);
                      return(FALSE);
                    }
                  R_File_Data = R_File_Data + (R_Size - 1);
		  ST_Display_Value (STDataRead, R_File_Data);
                  R_File_Size = R_File_Size +  (status);
                  ST_Display_Value (STDowSize, R_File_Size);

                  Time_Estimate = gtime(NULL) - e_timer;
                  ST_Display_Value (STElapsed, Time_Estimate);

                  if (Time_Estimate != 0)
                    {
                      Com_Rate = R_Com_Data / Time_Estimate;
                      Data_Rate = R_File_Data / Time_Estimate;
                      ST_Display_Value (STComRate, Com_Rate);
                      ST_Display_Value (STDataRate, Data_Rate);
                    }
                  else Data_Rate = 0;

                  if (R_Remaining != 0)
                             /* Decrement remaining byte count */
                    {
                      R_Remaining = R_Remaining - (R_Size - 1);
                      ST_Display_Value (STDowRem, R_Remaining);


                      if (Data_Rate != 0)
                        {
                          Time_Estimate = R_Remaining / Data_Rate;
                          ST_Display_Value (STRemTime, Time_Estimate);
                        }
                    }


                  Send_ACK();
                break;

              case 'T' :
                  if (R_buffer[1] == 'C')
                    {
                      ST_Display_String (STMsg, "*** Transfer Complete ***");
                      status = fclose (Data_File);

                      if (status == EOF)
                        {
                          ST_Display_String (STMsg, "Failure during close...aborting");
                          Send_Failure ("EError during close");
                          Check_Keep (Data_File, Name);
                          return(FALSE);
                        }

                      Send_ACK();
                      return(TRUE);
                    }
                else if (R_buffer [1] == 'I')
                  {
                    Send_ACK();
                    Process_File_Information();
                  }
                else if ((R_buffer [1] == 'f') && BP_Auto_Resume)
                              /* `Tf' Packet implies host failed the */
                  {          /* CRC check on a DOW resume */
			fclose (Data_File);       /* So...replace the file */
			Data_File = fopen(Name, "wb");
			if (Data_File == NULL)
			{
				Send_Failure ("CCannot create file");

				ST_Display_String (STMsg,
					"CRC check failed; cannot create file");
				return(FALSE);
			}

                    if (Our_FI != 0 || BP_Use_File_Size)
                      R_Remaining = R_Remaining + R_File_Size;

                    R_File_Size = 0;
                    ST_Display_String (STMsg, "CRC check failed; overwriting file");
			Resume_Flag = FALSE;
			e_timer = gtime(NULL);
			S_Com_Data = 0;
			R_Com_Data = 0;
                    Send_ACK();
                  }
                else
                  {
                    ST_Display_String (STMsg, "Invalid termination Packet...aborting");
                    Send_Failure ("NInvalid T Packet");
                    Check_Keep (Data_File, Name);
                    return(FALSE);
                  }
              break;

              case 'F' :
                  Send_ACK();
                  ST_Display_String (STMsg, "Failure Packet received...aborting");
                  Check_Keep (Data_File, Name);
                  return(FALSE);

            }

          }
        else
          {
            if (!Aborting)
              ST_Display_String (STMsg, "Download failure");
            Check_Keep (Data_File, Name);
            return(FALSE);
          }
    }

} /* Receive_File */

/* =================================================================== */

void BP_DLE_Seen()
{   /* DLE_Seen */

    int    i;

  /*
    Begin by getting the next character.  If it is <B> then enter the
    B_Protocol state.  Otherwise simply return.
  */

  Port_Update_Rate = 30;
/*********************
  ST_Initialize(0);
**********************/
  if (!read_byte())
	return;


  if (ch != 'B')
	return;

  SA_Next_to_ACK  = 0;    /* Initialize Send-ahead variables */
  SA_Next_to_Fill = 0;
  SA_Waiting      = 0;
  Aborting        = FALSE;
  Packet_Received = FALSE;

  /* Establish Data Block Size as a function of the Baud */
  /* The intent is to keep the per-Packet time to 4-5 seconds */

  PortBps = bps;       /* Apend FMR version */

  switch (PortBps) {
    case bps300:
        Def_BS = 1;
        Port_Update_Rate = 30;
	break;
    case bps600:
    case bps1200:
        Def_BS = 4;
        Port_Update_Rate = 120;
	break;
    case bps2400:
    case bps4800:
    case bps9600:
        Def_BS = 8;
        Port_Update_Rate = 240;
	break;
    }

  /*  <DLE><B> received; begin B Protocol */

  xoff_flag   = TRUE;

  R_Counter   = 0;
  S_Counter   = 0;
  R_File_Data = 0;
  S_File_Data = 0;
  R_Com_Data  = 0;
  S_Com_Data  =  (0);
  S_Packet_Count =  (0);
  R_Packet_Count =  (0);
  S_File_Size =  (0);
  R_File_Size =  (0);
  S_Error_Count =  (0);
  R_Error_Count =  (0);
	Resume_Flag = FALSE;

  if (Read_Packet (TRUE, FALSE))
      {
        /* Dispatch on the type of Packet just received */

        switch (R_buffer[0]) {
          case 'T':                      /* File Transfer Application */
                ST_Initialize(0);
		ST_Display_Value (STComRead, R_Com_Data);
		S_Com_Data = 0;
		R_Com_Data = 0;
		e_timer = gtime(NULL);

		switch (R_buffer[1]) {
                   case 'D' :
			ST_Display_String (STUpDow, "Downloading ");
			break;
                   case 'U' :
			ST_Display_String (STUpDow, "Uploading ");
			break;
                   default:
                       ST_Display_String (STMsg, "Unimplemented Transfer Function");
                       Send_Failure ("NUnimplemented Transfer function");
                       ST_Terminate();
                       return;
                 }

                 switch (R_buffer[2]) {
                   case 'A':
			ST_Display_String (STType, "ASCII");
			break;
                   case 'B':
			ST_Display_String (STType, "Binary");
			break;
                   default:
                       ST_Display_String (STMsg, "Unimplemented File Type");
                       Send_Failure ("NUnimplemented file type");
                       ST_Terminate();
                       return;
                 }

                 i = 2;
		 filename[0] = '\0';

                 while ( R_buffer[i] != 0 ) {
		    if ( i >= (R_Size - 1)) {
			filename[i - 2] = '\0';
			break;
		    }
		    i++;
		    filename[i-3] = R_buffer[i];
                 }

		 ST_Display_String (STFile, filename);
                 S_Packet_Count =  (0);
                 R_Packet_Count =  (0);

                 if (R_buffer[1] == 'U')
                   Send_File (filename);
                 else
                   Receive_File (filename);

                 ST_Terminate();
               break;

          case '+':          /* Received Transport Parameters Packet */
              Do_Transport_Parameters();
		break;

          default:
                   /* Unknown Packet; tell the host we don't know */
              Send_Failure ("NUnknown Packet Type");
        break;

        }  /* of case */

      }    /* of if Read_Packet then*/
} /* DLE_Seen */
