Metropoli BBS
VIEWER: xmsend.c MODE: TEXT (ASCII)
/*--------------------------------------------------------------------------*/
/*                                                                          */
/*                                                                          */
/*      ------------         Bit-Bucket Software, Co.                       */
/*      \ 10001101 /         Writers and Distributors of                    */
/*       \ 011110 /          Freely Available<tm> Software.                 */
/*        \ 1011 /                                                          */
/*         ------                                                           */
/*                                                                          */
/*              (C) Copyright 1987-96, Bit Bucket Software Co.              */
/*                                                                          */
/*                  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.260.    */
/*                                                                          */
/*    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:42/1491                        */
/* 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 = {0};
	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
	{
#ifdef ANSI_TIME_T
		ttmp->timedate = args->save_filetime.oneword.timedate - ANSI_TIME_T_DELTA;
#else
		ttmp->timedate = args->save_filetime.oneword.timedate;
#endif
		ttmp->SLO = (unsigned char) (((cur_baud.rate_value >= 9600L) && !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) dfind (&dta, NULL, 2);
}

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

typedef struct
{
	char *state_name;
	int (*state_func) (XMARGSP);
} XSTATES, *XSTATEP;

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

int 
XSInit (XMARGSP args)
{
	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, read_binary, 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 (filewin, 1, 2);
		sb_puts (filewin, 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 ((int) args->control);
}

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

	/* 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 (args->result == SUCCESS)
	{
		long lTime, lSize;

		lSize = args->filelen - args->prev_bytes;
		lTime = throughput (1, (unsigned long) lSize);
		status_line ("%s: %s", MSG_TXT (M_FILE_SENT), args->filename);
		update_files (1, args->filename, lSize, lTime, 0);
	}

	return (args->result);
}

void 
XSSetVars (XMARGSP args)
{
	if (no_sealink)
	{
		args->options.SLO = 0;
		args->options.Resync = 0;
	}
	else
	{
		args->options.SLO = ((cur_baud.rate_value >= 9600L) && !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 ((STATEP) 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 ((STATEP) 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];
	long k;

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

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

	if (args->options.SLO)
	{
		if ((!((args->SendBLK - 1) & 0x1f)) || ((args->SendBLK - 1) > args->LastBlk))
		{
			if (fullscreen && un_attended)
			{
				elapse_time ();
				sb_move (filewin, 2, 2);
				TmpPtr = show_num (args, args->SendBLK - 1);
				sb_puts (filewin, TmpPtr);
				(void) sb_putc (filewin, ':');
				TmpPtr = show_num (args, args->ACKBLK);
				sb_puts (filewin, TmpPtr);
				sb_puts (filewin, " *Overdrive*  ");
				sb_move (filewin, 2, 69);
				sb_puts (filewin, 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 (filewin, 2, 2);
			TmpPtr = show_num (args, args->SendBLK - 1);
			sb_puts (filewin, TmpPtr);
			(void) sb_putc (filewin, ':');
			TmpPtr = show_num (args, args->ACKBLK);
			sb_puts (filewin, TmpPtr);

			sb_puts (filewin, "              ");
			sb_move (filewin, 2, 69);
			sb_puts (filewin, 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 ACEnd (XMARGSP);
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);

typedef struct
{
	char *state_name;
	int (*state_func) (XMARGSP);
} ASTATES, *ASTATEP;

ASTATES 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)
{
	args->result = 0;
	return ((int) args->control);
}

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

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 ((STATEP) ACKNAK_Check, args, AC0);
}

int 
Receive_Resync (long *resync_block)
{
	unsigned char resyncit[30];
	unsigned char *p;
	unsigned char a, b;
	unsigned short 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 %04hx/%04hx", 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 ()
{
	long window;

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

	return (int) window;
}
[ RETURN TO DIRECTORY ]