/*
 *  UMODEM -- Implements the "CP/M User's Group XMODEM" protocol, 
 *	      the TERM II File Transfer Protocol (FTP) Number 1,
 *	      and the TERM II File Transfer Protocol Number 4 for
 *            packetized file up/downloading.    
 *
 *    Note: UNIX System-Dependent values are indicated by the string [SD]
 *          in a comment field on the same line as the values.
 *
 *
 *         -- Lauren Weinstein, 6/81
 *	   -- (Version 2.0) Modified for JHU/UNIX by Richard Conn, 8/1/81
 *	   -- Version 2.1 Mods by Richard Conn, 8/2/81
 *		. File Size Included on Send Option
 *	   -- Version 2.2 Mods by Richard Conn, 8/2/81
 *		. Log File Generation and Option Incorporated
 *	   -- Version 2.3 Mods by Richard Conn, 8/3/81
 *		. TERM II FTP 1 Supported
 *		. Error Log Reports Enhanced
 *		. CAN Function Added to FTP 3
 *		. 'd' Option Added to Delete umodem.log File before starting
 *	   -- Version 2.4 Mods by Richard Conn, 8/4/81
 *		. 16K-extent sector number check error corrected
 *		. Count of number of received sectors added
 *	   -- Version 2.5 Mods by Richard Conn, 8/5/81
 *		. ARPA Net Flag added
 *		. ARPA Net parameter ('a') added to command line
 *		. ARPA Net BIS, BIE, BOS, BOE added
 *		. ARPA Net FFH escape added
 *	   -- Version 2.6 Mods by Bennett Marks, 8/21/81 (Bucky @ CCA-UNIX)
 *		. mods for UNIX V7 (Note: for JHU compilation define
 *		  the variable JHU  during 'cc'
 *		. added 'mungmode' flag to protect from inadvertant
 *		  overwrite on file receive
 *		. changed timeout handling prior to issuing checksum
 *	   -- Version 2.7 Mods by Richard Conn, 8/25/81 (rconn @ BRL)
 *		. correct minor "ifndef" error in which ifndef had no arg
 *		. restructured "ifdef" references so that other versions
 *		  of UNIX than Version 7 and JHU can be easily incorporated;
 *		  previous ifdef references were for JHU/not JHU;
 *		  to compile under Version 7 or JHU UNIX, the following
 *		  command lines are recommended:
 *			"cc umodem.c -o umodem -DVER7" for Version 7
 *			"cc -7 umodem.c -o umodem -DJHU" for JHU
 *		. added 'y' file status display option; this option gives
 *		  the user an estimate of the size of the target file to
 *		  send from the UNIX system in terms of CP/M records (128
 *		  bytes) and Kbytes (1024 byte units)
 *		. added '7' option which modifies the transmission protocols
 *		  for 7 significant bits rather than 8; modifies both FTP 1
 *		  and FTP 3
 *	   -- Version 2.8 Mods by Richard Conn, 8/28/81
 *		. corrected system-dependent reference to TIOCSSCR (for
 *		  disabling page mode) and created the PAGEMODE flag which
 *		  is to be set to TRUE to enable this
 *		. added -4 option which engages TERM II, FTP 4 (new release)
 *	   -- Version 2.9 Mods by Richard Conn, 9/1/81
 *		. internal documentation on ARPA Net protocols expanded
 *		. possible operator precedence problem with BITMASK corrected
 *		  by redundant parentheses
 *	   -- Version 3.0 Mods by Lauren Weinstein, 9/14/81
 *              . fixed bug in PAGEMODE defines (removed PAGEMODE define
 *	          line; now should be compiled with "-DPAGEMODE" if
 *		  Page Mode is desired)
 *		. included forward declaration of ttyname() to avoid problems
 *		  with newer V7 C compilers
 *         -- Version 3.1 Mods by Lauren Weinstein, 4/17/82
 *		. avoids sending extraneous last sector when file EOF
 *	          occurs on an exact sector boundary
 *	   -- Version 3.2 Mods by Michael M Rubenstein, 5/26/83
 *	        . fixed bug in readbyte.  assumed that int's are ordered
 *		  from low significance to high
 *		. added LOGDEFAULT define to allow default logging to be
 *		  off.  compile with -DLOGDEFAULT=0 to set default to no
 *		  no logging.
 *
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>

/*  JHU UNIX tty parameter file  */
#ifdef JHU
#include <stty.h>
#endif

/*  Version 7 UNIX tty parameter file  */
#ifdef VER7
#include <sgtty.h>
#endif

/* log default define */
#ifndef LOGDEFAULT
#define LOGDEFAULT	1
#endif

#include <signal.h>

#define	     VERSION	32	/* Version Number */
#define	     TRUE	1		
#define      FALSE      0

/*  Compile with "-DPAGEMODE" if Page Mode (TIOCSSCR) is supported on your
 *  UNIX system.  If it is supported, make sure that TIOCSSCR is the
 *  correct name for Page Mode engagement.
 */

/*  ASCII Constants  */
#define      SOH  	001 
#define	     STX	002
#define	     ETX	003
#define      EOT	004
#define	     ENQ	005
#define      ACK  	006
#define	     LF		012   /* Unix LF/NL */
#define	     CR		015  
#define      NAK  	025
#define	     SYN	026
#define	     CAN	030
#define	     ESC	033
#define	     CTRLZ	032   /* CP/M EOF for text (usually!) */

/*  UMODEM Constants  */
#define      TIMEOUT  	-1
#define      ERRORMAX  	10    /* maximum errors tolerated */
#define      RETRYMAX  	10    /* maximum retries to be made */
#define	     BBUFSIZ	128   /* buffer size -- do not change! */

/*  [SD] Mode for Created Files  */
#define      CREATMODE	0644  /* mode for created files */

/*  ARPA Net Constants  */
/*	The following constants are used to communicate with the ARPA
 *	Net SERVER TELNET and TIP programs.  These constants are defined
 *	as follows:
 *		IAC			<-- Is A Command; indicates that
 *						a command follows
 *		WILL/WONT		<-- Command issued to SERVER TELNET
 *						(Host); WILL issues command
 *						and WONT issues negative of
 *						the command
 *		DO/DONT			<-- Command issued to TIP; DO issues
 *						command and DONT issues
 *						negative of the command
 *		TRBIN			<-- Transmit Binary Command
 *	Examples:
 *		IAC WILL TRBIN	<-- Host is configured to transmit Binary
 *		IAC WONT TRBIN	<-- Host is configured NOT to transmit binary
 *		IAC DO TRBIN	<-- TIP is configured to transmit Binary
 *		IAC DONT TRBIN	<-- TIP is configured NOT to transmit binary
 */
#define	     IAC	0377	/* Is A Command */
#define	     DO		0375	/* Command to TIP */
#define	     DONT	0376	/* Negative of Command to TIP */
#define	     WILL	0373	/* Command to SERVER TELNET (Host) */
#define	     WONT	0374	/* Negative of Command to SERVER TELNET */
#define	     TRBIN	0	/* Transmit Binary Command */

/*  JHU UNIX structures  */
#ifdef JHU
struct sttybuf ttys, ttysnew, ttystemp;    /* for stty terminal mode calls */
#endif

/*  Version 7 UNIX structures  */
#ifdef VER7
struct sgttyb  ttys, ttysnew, ttystemp;    /* for stty terminal mode calls */
#endif

struct stat statbuf;  	/* for terminal message on/off control */
FILE *LOGFP, *fopen();
char buff[BBUFSIZ];

#ifdef JHU
int wason;
#endif

#ifdef VER7
int pagelen;
char *ttyname();  /* forward declaration for C */
#endif

char *tty;
char XMITTYPE;
int ARPA, RECVFLAG, SENDFLAG, FTP1, PMSG, DELFLAG, LOGFLAG, MUNGMODE;
int STATDISP, BIT7, BITMASK;
int delay;

alarmfunc();

main(argc, argv)
int argc;
char **argv;
{
	char *logfile;
	int index;
	char flag;

	logfile = "umodem.log";  /* Name of LOG File */

	printf("\nUMODEM Version %d.%d", VERSION/10, VERSION%10);
	printf(" -- UNIX-Based Remote File Transfer Facility\n");

	if (argc < 3 || *argv[1] != '-')
	{  printf("\nUsage:  \n\tumodem ");
		printf("-[rb!rt!sb!st][p][l][1][a][m][d][y][7][4]");
		printf(" filename\n");
	   printf("\n");
	   printf("\n\trb <-- Receive Binary");
	   printf("\n\trt <-- Receive Text");
	   printf("\n\tsb <-- Send Binary");
	   printf("\n\tst <-- Send Text");
	   printf("\n\tp  <-- Turn ON Parameter Display");

#if LOGDEFAULT
	   printf("\n\tl  <-- (ell) Turn OFF LOG File Entries");
#else
	   printf("\n\tl  <-- (ell) Turn ON LOG File Entries");
#endif

	   printf("\n\t1  <-- (one) Employ TERM II FTP 1");
	   printf("\n\ta  <-- Turn ON ARPA Net Flag");
	   printf("\n\tm  <-- Allow file overwiting on receive");
	   printf("\n\td  <-- Delete umodem.log File before starting");
	   printf("\n\ty  <-- Display file status (size) information only");
	   printf("\n\t7  <-- Enable 7-bit transfer mask");
	   printf("\n\t4  <-- Enable TERM II FTP 4");
	   printf("\n");
		exit(-1);
	}

	index = 1;  /* set index for loop */
	delay = 3;  /* assume FTP 3 delay */
	PMSG = FALSE;  /* turn off flags */
	FTP1 = FALSE;  /* assume FTP 3 (CP/M UG XMODEM2) */
	RECVFLAG = FALSE;  /* not receive */
	SENDFLAG = FALSE;  /* not send either */
	XMITTYPE = 't';  /* assume text */
	DELFLAG = FALSE;  /* do NOT delete log file before starting */
	LOGFLAG = LOGDEFAULT;
	ARPA = FALSE;  /* assume not on ARPA Net */
	MUNGMODE = FALSE; /* protect files from overwriting */
	STATDISP = FALSE;  /* assume not a status display */
	BIT7 = FALSE;  /* assume 8-bit communication */
	while ((flag = argv[1][index++]) != '\0')
	    switch (flag) {
		case 'a' : ARPA = TRUE;  /* set ARPA Net */
			   break;
		case 'p' : PMSG = TRUE;  /* print all messages */
			   break;
		case '1' : FTP1 = TRUE;  /* select FTP 1 */
			   delay = 5;  /* FTP 1 delay constant */
			   printf("\nUMODEM:  TERM II FTP 1 Selected\n");
			   break;
		case 'd' : DELFLAG = TRUE;  /* delete log file first */

			   break;
		case 'l' : LOGFLAG = ~LOGDEFAULT;  /* turn off log report */
			   break;
		case 'r' : RECVFLAG = TRUE;  /* receive file */
			   XMITTYPE = gettype(argv[1][index++]);  /* get t/b */
			   break;
		case 's' : SENDFLAG = TRUE;  /* send file */
			   XMITTYPE = gettype(argv[1][index++]);
			   break;
		case 'm' : MUNGMODE = TRUE; /* allow overwriting of files */
			   break;
		case 'y' : STATDISP = TRUE;  /* display file status */
			   break;
		case '7' : BIT7 = TRUE;  /* transfer only 7 bits */
			   break;
		case '4' : FTP1 = TRUE;  /* select FTP 1 (varient) */
			   BIT7 = TRUE;  /* transfer only 7 bits */
			   delay = 5;  /* FTP 1 delay constant */
			   printf("\nUMODEM:  TERM II FTP 4 Selected\n");
			   break;
		default  : error("Invalid Flag", FALSE);
		}

	if (BIT7 && (XMITTYPE == 'b'))
	{  printf("\nUMODEM:  Fatal Error -- Both 7-Bit Transfer and ");
	   printf("Binary Transfer Selected");
	   exit(-1);  /* error exit to UNIX */
	}

	if (BIT7)  /* set MASK value */
	   BITMASK = 0177;  /* 7 significant bits */
	else
	   BITMASK = 0377;  /* 8 significant bits */

	if (PMSG)
	   { printf("\nSupported File Transfer Protocols:");
	     printf("\n\tTERM II FTP 1");
	     printf("\n\tCP/M UG XMODEM2 (TERM II FTP 3)");
	     printf("\n\tTERM II FTP 4");
	     printf("\n\n");
	   }

	if (LOGFLAG)
	   { if (!DELFLAG)
		LOGFP = fopen(logfile, "a");  /* append to LOG file */
	     else
		LOGFP = fopen(logfile, "w");  /* new LOG file */
	     fprintf(LOGFP,"\n\n++++++++\n");
	     fprintf(LOGFP,"\nUMODEM Version %d.%d\n", VERSION/10, VERSION%10);
	     printf("\nUMODEM:  LOG File '%s' is Open\n", logfile);
	   }

	if (STATDISP) yfile(argv[2]);  /* status of a file */

	if (RECVFLAG && SENDFLAG)
		error("Both Send and Receive Functions Specified", FALSE);
	if (!RECVFLAG && !SENDFLAG)
		error("Neither Send nor Receive Functions Specified", FALSE);

	if (RECVFLAG)
	{  if(open(argv[2], 0) != -1)  /* possible abort if file exists */
	   {	printf("\nUMODEM:  Warning -- Target File Exists\n");
		if( MUNGMODE == FALSE )
			error("Fatal - Can't overwrite file\n",FALSE);
		printf("UMODEM:  Overwriting Target File\n");
	   }
	   rfile(argv[2]);  /* receive file */
	}
	else
		sfile(argv[2]);  /* send file */

}

gettype(ichar)
char ichar;
{
	if (ichar == 't') return(ichar);
	if (ichar == 'b') return(ichar);
	error("Invalid Send/Receive Parameter - not t or b", FALSE);
	return;
}

/* set tty modes for UMODEM transfers */
setmodes()
{

/*  Device characteristics for JHU UNIX  */
#ifdef JHU	
	if (gtty(0, &ttys) < 0)  /* get current tty params */
		error("Can't get TTY Parameters", TRUE);

	tty = ttyname(0);  /* identify current tty */

	/* duplicate current modes in ttysnew structure */
	ttysnew.ispeed = ttys.ispeed;	/* copy input speed */
	ttysnew.ospeed = ttys.ospeed;	/* copy output speed */
	ttysnew.xflags = ttys.xflags;	/* copy JHU/UNIX extended flags */
	ttysnew.mode   = ttys.mode;	/* copy standard terminal flags */

	ttysnew.mode |= RAW;    /* set for RAW Mode */
			/* This ORs in the RAW mode value, thereby
			   setting RAW mode and leaving the other
			   mode settings unchanged */
	ttysnew.mode &= ~ECHO;  /* set for no echoing */
			/* This ANDs in the complement of the ECHO
			   setting (for NO echo), thereby leaving all
			   current parameters unchanged and turning
			   OFF ECHO only */
	ttysnew.mode &= ~XTABS;  /* set for no tab expansion */
	ttysnew.mode &= ~LCASE;  /* set for no upper-to-lower case xlate */
	ttysnew.mode |= ANYP;  /* set for ANY Parity */
	ttysnew.mode &= ~NL3;  /* turn off ALL delays - new line */
	ttysnew.mode &= ~TAB3; /* turn off tab delays */
	ttysnew.mode &= ~CR3;  /* turn off CR delays */
	ttysnew.mode &= ~FF1;  /* turn off FF delays */
	ttysnew.mode &= ~BS1;  /* turn off BS delays */
	/* the following are JHU/UNIX xflags settings; they are [SD] */
	ttysnew.xflags &= ~PAGE;  /* turn off paging */
	ttysnew.xflags &= ~STALL;  /* turn off ^S/^Q recognition */
	ttysnew.xflags &= ~TAPE;  /* turn off ^S/^Q input control */
	ttysnew.xflags &= ~FOLD;  /* turn off CR/LF folding at col 72 */
	ttysnew.xflags |= NB8;  /* turn on 8-bit input/output */

	if (stty(0, &ttysnew) < 0)  /* set new params */
		error("Can't set new TTY Parameters", TRUE);

	if (stat(tty, &statbuf) < 0)  /* get tty status */ 
		error("Can't get your TTY Status", TRUE);

	if (statbuf.st_mode&011)  /* messages are on [SD] */
	{	wason = TRUE;
		if (chmod(tty, 020600) < 0)  /* turn off tty messages [SD] */
			error("Can't change TTY Mode", TRUE);
	}	
	else
		wason = FALSE;  /* messages are already off */
#endif		

/*  Device characteristics for Version 7 UNIX  */
#ifdef VER7
	if (ioctl(0,TIOCGETP,&ttys)<0)  /* get tty params [V7] */
		error("Can't get TTY Parameters", TRUE);
	tty = ttyname(0);  /* identify current tty */
	
	/* transfer current modes to new structure */
	ttysnew.sg_ispeed = ttys.sg_ispeed;	/* copy input speed */
	ttysnew.sg_ospeed = ttys.sg_ospeed;	/* copy output speed */
	ttysnew.sg_erase  = ttys.sg_erase;	/* copy erase flags */
	ttysnew.sg_flags  = ttys.sg_flags;	/* copy flags */
 	ttysnew.sg_kill   = ttys.sg_kill;	/* copy std terminal flags */
	

	ttysnew.sg_flags |= RAW;    /* set for RAW Mode */
			/* This ORs in the RAW mode value, thereby
			   setting RAW mode and leaving the other
			   mode settings unchanged */
	ttysnew.sg_flags &= ~ECHO;  /* set for no echoing */
			/* This ANDs in the complement of the ECHO
			   setting (for NO echo), thereby leaving all
			   current parameters unchanged and turning
			   OFF ECHO only */
	ttysnew.sg_flags &= ~XTABS;  /* set for no tab expansion */
	ttysnew.sg_flags &= ~LCASE;  /* set for no upper-to-lower case xlate */
	ttysnew.sg_flags |= ANYP;  /* set for ANY Parity */
	ttysnew.sg_flags &= ~NL3;  /* turn off ALL delays - new line */
	ttysnew.sg_flags &= ~TAB2; /* turn off tab delays */
	ttysnew.sg_flags &= ~CR3;  /* turn off CR delays */
	ttysnew.sg_flags &= ~FF1;  /* turn off FF delays */
	ttysnew.sg_flags &= ~BS1;  /* turn off BS delays */
	ttysnew.sg_flags &= ~TANDEM;  /* turn off flow control */

#ifdef PAGEMODE
	/* make sure page mode is off */
	ioctl(0,TIOCSSCR,&pagelen);	/*  [SD]  */
#endif
	
	/* set new paramters */
	if (ioctl(0,TIOCSETP,&ttysnew) < 0)
		error("Can't set new TTY Parameters", TRUE);

	if (stat(tty, &statbuf) < 0)  /* get tty status */ 
		error("Can't get your TTY Status", TRUE);
#endif	

	if (PMSG)
		{ printf("\nUMODEM:  TTY Device Parameters Altered");
		  ttyparams();  /* print tty params */
		}

	if (ARPA)  /* set 8-bit on ARPA Net */
		setarpa();

	return;
}

/*  set ARPA Net for 8-bit transfers  */
setarpa()
{
	sendbyte(IAC);	/* Is A Command */
	sendbyte(WILL);	/* Command to SERVER TELNET (Host) */
	sendbyte(TRBIN);	/* Command is:  Transmit Binary */

	sendbyte(IAC);	/* Is A Command */
	sendbyte(DO);	/* Command to TIP */
	sendbyte(TRBIN);	/* Command is:  Transmit Binary */

	sleep(3);  /* wait for TIP to configure */

	return;
}

/* restore normal tty modes */
restoremodes(errcall)
int errcall;
{
	if (ARPA)  /* if ARPA Net, reconfigure */
		resetarpa();

/*  Device characteristic restoration for JHU UNIX  */
#ifdef JHU
	if (wason)  /* if messages were on originally */
		if (chmod(tty, 020611) < 0)  /*  [SD]  */
			error("Can't change TTY Mode", FALSE);

	if (stty(0, &ttys) < 0)  /* restore original tty modes */
		{ if (!errcall)
		   error("RESET - Can't restore normal TTY Params", FALSE);
		else
		   { printf("UMODEM:  ");
		     printf("RESET - Can't restore normal TTY Params\n");
		   }
		}
#endif

/*  Device characteristic restoration for Version 7 UNIX  */
#ifdef VER7
	if (ioctl(0,TIOCSETP,&ttys) < 0)
		{ if (!errcall)
		   error("RESET - Can't restore normal TTY Params", FALSE);
		else
		   { printf("UMODEM:  ");
		     printf("RESET - Can't restore normal TTY Params\n");
		   }
		}
#endif

	if (PMSG)
		{ printf("\nUMODEM:  TTY Device Parameters Restored");
		  ttyparams();  /* print tty params */
		}

	return;
}

/* reset the ARPA Net */
resetarpa()
{
	sendbyte(IAC);	/* Is A Command */
	sendbyte(WONT);	/* Negative Command to SERVER TELNET (Host) */
	sendbyte(TRBIN);	/* Command is:  Don't Transmit Binary */

	sendbyte(IAC);	/* Is A Command */
	sendbyte(DONT);	/* Negative Command to TIP */
	sendbyte(TRBIN);	/* Command is:  Don't Transmit Binary */

	return;
}

/* print error message and exit; if mode == TRUE, restore normal tty modes */
error(msg, mode)
char *msg;
int mode;
{
	if (mode)
		restoremodes(TRUE);  /* put back normal tty modes */
	printf("UMODEM:  %s\n", msg);
	if (LOGFLAG)
	{   fprintf(LOGFP, "UMODEM Fatal Error:  %s\n", msg);
	    fclose(LOGFP);
	}
	exit(-1);
}

/**  print status (size) of a file  **/
yfile(name)
char *name;
{
	printf("UMODEM File Status Display for %s\n", name);
	if (LOGFLAG) fprintf(LOGFP,"UMODEM File Status Display for %s\n",
	  name);

	if (open(name,0) < 0)
	{  printf("File %s does not exist\n", name);
	   if (LOGFLAG) fprintf(LOGFP,"File %s does not exist\n", name);
	   exit(-1);  /* error exit to UNIX */
	}

	prfilestat(name);  /* print status */
	printf("\n");
	if (LOGFLAG)
	{  fprintf(LOGFP,"\n");
	   fclose(LOGFP);
	}

	exit(0);  /* exit to UNIX -- no error */
}

/**  receive a file  **/
rfile(name)
char *name;
{
	char mode;
	int fd, j, firstchar, sectnum, sectcurr, tmode;
	int sectcomp, errors, errorflag, recfin;
	register int bufctr, checksum;
	register int c;
	int errorchar, fatalerror, startstx, inchecksum, endetx, endenq;
	long recvsectcnt;

	mode = XMITTYPE;  /* set t/b mode */
	if ((fd = creat(name, CREATMODE)) < 0)
	  	error("Can't create file for receive", FALSE);
	setmodes();  /* setup tty modes for xfer */
	printf("\r\nUMODEM:  File Name: %s", name);
	if (LOGFLAG)
	{    fprintf(LOGFP, "\n----\nUMODEM Receive Function\n");
	     fprintf(LOGFP, "File Name: %s\n", name);
	     if (FTP1)
		if (!BIT7)
	         fprintf(LOGFP, "TERM II File Transfer Protocol 1 Selected\n");
		else
		 fprintf(LOGFP, "TERM II File Transfer Protocol 4 Selected\n");
	     else
		fprintf(LOGFP,
		  "TERM II File Transfer Protocol 3 (CP/M UG) Selected\n");
	     if (BIT7)
		fprintf(LOGFP, "7-Bit Transmission Enabled\n");
	     else
		fprintf(LOGFP, "8-Bit Transmission Enabled\n");
	}
	printf("\r\nUMODEM:  ");
	if (BIT7)
		printf("7-Bit");
	else
		printf("8-Bit");
	printf(" Transmission Enabled");
	printf("\r\nUMODEM:  Ready to RECEIVE File\r\n");

	recfin = FALSE;
	sectnum = errors = 0;
	fatalerror = FALSE;  /* NO fatal errors */
	recvsectcnt = 0;  /* number of received sectors */

	if (mode == 't')
		tmode = TRUE;
	else
		tmode = FALSE;

	if (FTP1)
	{
	  while (readbyte(4) != SYN);
	  sendbyte(ACK);  /* FTP 1 Sync */
	}
	else sendbyte(NAK);  /* FTP 3 Sync */

        do
        {   errorflag = FALSE;
            do {
                  firstchar = readbyte(6);
            } while ((firstchar != SOH) && (firstchar != EOT) && (firstchar 
		     != TIMEOUT));
            if (firstchar == TIMEOUT)
	    {  if (LOGFLAG)
		fprintf(LOGFP, "Timeout on Sector %d\n", sectnum);
               errorflag = TRUE;
	    }

            if (firstchar == SOH)
	    {  if (FTP1) readbyte(5);  /* discard leading zero */
               sectcurr = readbyte(delay);
               sectcomp = readbyte(delay);
	       if (FTP1) startstx = readbyte(delay);  /* get leading STX */
               if ((sectcurr + sectcomp) == BITMASK)
               {  if (sectcurr == ((sectnum+1)&BITMASK))
		  {  checksum = 0;
		     for (j = bufctr = 0; j < BBUFSIZ; j++)
	      	     {  buff[bufctr] = c = readbyte(delay);
		        checksum = ((checksum+c)&BITMASK);
			if (!tmode)  /* binary mode */
			{  bufctr++;
		           continue;
		        }
			if (c == CR)
			   continue;  /* skip CR's */
			if (c == CTRLZ)  /* skip CP/M EOF char */
			{  recfin = TRUE;  /* flag EOF */
		           continue;
		        }
		        if (!recfin)
			   bufctr++;
		     }
		     if (FTP1) endetx = readbyte(delay);  /* get ending ETX */
		     inchecksum = readbyte(delay);  /* get checksum */
		     if (FTP1) endenq = readbyte(delay); /* get ENQ */
		     if (checksum == inchecksum)  /* good checksum */
		     {  errors = 0;
			recvsectcnt++;
		        sectnum = sectcurr;  /* update sector counter */
			if (write(fd, buff, bufctr) < 0)
			   error("File Write Error", TRUE);
		        else
			{  if (FTP1) sendbyte(ESC);  /* FTP 1 requires <ESC> */
			   sendbyte(ACK);
			}
		     }
		     else
		     {  if (LOGFLAG)
				fprintf(LOGFP, "Checksum Error on Sector %d\n",
				sectnum);
		        errorflag = TRUE;
		     }
                  }
                  else
                  { if (sectcurr == sectnum)
                    {  while(readbyte(3) != TIMEOUT);
		       if (FTP1) sendbyte(ESC);  /* FTP 1 requires <ESC> */
            	       sendbyte(ACK);
                    }
                    else
		    {  if (LOGFLAG)
			{ fprintf(LOGFP, "Phase Error - Received Sector is ");
			  fprintf(LOGFP, "%d while Expected Sector is %d\n",
			   sectcurr, ((sectnum+1)&BITMASK));
			}
			errorflag = TRUE;
			fatalerror = TRUE;
			if (FTP1) sendbyte(ESC);  /* FTP 1 requires <ESC> */
			sendbyte(CAN);
		    }
	          }
           }
           else
	   {  if (LOGFLAG)
		fprintf(LOGFP, "Header Sector Number Error on Sector %d\n",
		   sectnum);
               errorflag = TRUE;
	   }
        }
	if (FTP1 && !errorflag)
	{  if (startstx != STX)
	   {  errorflag = TRUE;  /* FTP 1 STX missing */
	      errorchar = STX;
	   }
	   if (endetx != ETX)
	   {  errorflag = TRUE;  /* FTP 1 ETX missing */
	      errorchar = ETX;
	   }
	   if (endenq != ENQ)
	   {  errorflag = TRUE;  /* FTP 1 ENQ missing */
	      errorchar = ENQ;
	   }
	   if (errorflag && LOGFLAG)
	   {  fprintf(LOGFP, "Invalid Packet-Control Character:  ");
	      switch (errorchar) {
		case STX : fprintf(LOGFP, "STX"); break;
		case ETX : fprintf(LOGFP, "ETX"); break;
		case ENQ : fprintf(LOGFP, "ENQ"); break;
		default  : fprintf(LOGFP, "Error"); break;
	      }
	      fprintf(LOGFP, "\n");
	   }
	}
        if (errorflag == TRUE)
        {  errors++;
	   while (readbyte(3) != TIMEOUT);
           sendbyte(NAK);
        }
  }
  while ((firstchar != EOT) && (errors != ERRORMAX) && !fatalerror);
  if ((firstchar == EOT) && (errors < ERRORMAX))
  {  if (!FTP1) sendbyte(ACK);
     close(fd);
     restoremodes(FALSE);  /* restore normal tty modes */
     if (FTP1)
	while (readbyte(3) != TIMEOUT);  /* flush EOT's */
     sleep(3);  /* give other side time to return to terminal mode */
     if (LOGFLAG)
     {  fprintf(LOGFP, "\nReceive Complete\n");
	fprintf(LOGFP,"Number of Received CP/M Records is %ld\n", recvsectcnt);
        fclose(LOGFP);
     }
     printf("\n");
     exit(0);
  }
  else
  {  if (LOGFLAG && FTP1 && fatalerror) fprintf(LOGFP,
	"Synchronization Error");
     error("TIMEOUT -- Too Many Errors", TRUE);
  }
}

/**  send a file  **/
sfile(name)
char *name;
{
	char mode;
	int fd, charval, attempts;
	int nlflag, sendfin, tmode;
	register int bufctr, checksum, sectnum;
	char c;
	int sendresp;  /* response char to sent block */

	mode = XMITTYPE;  /* set t/b mode */
	if ((fd = open(name, 0)) < 0)
	{  if (LOGFLAG) fprintf(LOGFP, "Can't Open File\n");
     	   error("Can't open file for send", FALSE);
	}
	setmodes();  /* setup tty modes for xfer */	
	printf("\r\nUMODEM:  File Name: %s", name);
	if (LOGFLAG)
	{   fprintf(LOGFP, "\n----\nUMODEM Send Function\n");
	    fprintf(LOGFP, "File Name: %s\n", name);
	}
	prfilestat(name);  /* print file size statistics */
	if (LOGFLAG)
	{  if (FTP1)
	      if (!BIT7)
		fprintf(LOGFP, "TERM II File Transfer Protocol 1 Selected\n");
	      else
		fprintf(LOGFP, "TERM II File Transfer Protocol 4 Selected\n");
	   else
		fprintf(LOGFP,
		   "TERM II File Transfer Protocol 3 (CP/M UG) Selected\n");
	   if (BIT7)
		fprintf(LOGFP, "7-Bit Transmission Enabled\n");
	   else
		fprintf(LOGFP, "8-Bit Transmission Enabled\n");
	}
	printf("\r\nUMODEM:  ");
	if (BIT7)
		printf("7-Bit");
	else
		printf("8-Bit");
	printf(" Transmission Enabled");
	printf("\r\nUMODEM:  Ready to SEND File\r\n");

	if (mode == 't')
	   tmode = TRUE;
	else
	   tmode = FALSE;

        sendfin = nlflag = FALSE;
  	attempts = 0;

	if (FTP1)
	{  sendbyte(SYN);  /* FTP 1 Synchronize with Receiver */
	   while (readbyte(5) != ACK)
	   {  if(++attempts > RETRYMAX*6) error("Remote System Not Responding",
		TRUE);
	      sendbyte(SYN);
	   }
	}
	else
	{  while (readbyte(30) != NAK)  /* FTP 3 Synchronize with Receiver */
	   if (++attempts > RETRYMAX) error("Remote System Not Responding",
		TRUE);
	}

	sectnum = 1;  /* first sector number */
	attempts = 0;

        do 
	{   for (bufctr=0; bufctr < BBUFSIZ;)
	    {   if (nlflag)
	        {  buff[bufctr++] = LF;  /* leftover newline */
	           nlflag = FALSE;
	        }
	        if ((charval = read(fd, &c, 1)) < 0)
		   error("File Read Error", TRUE);
		if (charval == 0)  /* EOF for read */	
		{  sendfin = TRUE;  /* this is the last sector */
		   if (!bufctr)  /* if EOF on sector boundary */
		      break;  /* avoid sending extra sector */
		   if (tmode)
		      buff[bufctr++] = CTRLZ;  /* Control-Z for CP/M EOF */
	           else
		      bufctr++;
		   continue;
	        }
		if (tmode && c == LF)  /* text mode & Unix newline? */
	    	{  if (c == LF)  /* Unix newline? */
		   {  buff[bufctr++] = CR;  /* insert carriage return */
		      if (bufctr < BBUFSIZ)
	                 buff[bufctr++] = LF;  /* insert Unix newline */
	 	      else
		         nlflag = TRUE;  /* insert newline on next sector */
		   }
		   continue;
	   	}	
		buff[bufctr++] = c;  /* copy the char without change */
	    }
            attempts = 0;
	
	    if (!bufctr)  /* if EOF on sector boundary */
   	       break;  /* avoid sending empty sector */

            do
            {   sendbyte(SOH);  /* send start of packet header */
		if (FTP1) sendbyte(0);  /* FTP 1 Type 0 Packet */
                sendbyte(sectnum);  /* send current sector number */
                sendbyte(-sectnum-1);  /* and its complement */
		if (FTP1) sendbyte(STX);  /* send STX */
                checksum = 0;  /* init checksum */
                for (bufctr=0; bufctr < BBUFSIZ; bufctr++)
                {  sendbyte(buff[bufctr]);  /* send the byte */
		   if (ARPA && (buff[bufctr]==0xff))  /* ARPA Net FFH esc */
			sendbyte(buff[bufctr]);  /* send 2 FFH's for one */
                   checksum = ((checksum+buff[bufctr])&BITMASK);
	        }
/*		while (readbyte(3) != TIMEOUT);   flush chars from line */
		if (FTP1) sendbyte(ETX);  /* send ETX */
                sendbyte(checksum);  /* send the checksum */
		if (FTP1) sendbyte(ENQ);  /* send ENQ */
                attempts++;
		if (FTP1)
		{  sendresp = NAK;  /* prepare for NAK */
		   if (readbyte(10) == ESC) sendresp = readbyte(10);
		}
		else
		   sendresp = readbyte(10);  /* get response */
		if ((sendresp != ACK) && LOGFLAG)
		   { fprintf(LOGFP, "Non-ACK Received on Sector %d\n",
		      sectnum);
		     if (sendresp == TIMEOUT)
			fprintf(LOGFP, "This Non-ACK was a TIMEOUT\n");
		   }
            }   while((sendresp != ACK) && (attempts != RETRYMAX));
            sectnum++;  /* increment to next sector number */
    }  while (!sendfin && (attempts != RETRYMAX));

    if (attempts == RETRYMAX)
	error("Remote System Not Responding", TRUE);

    attempts = 0;
    if (FTP1)
	while (attempts++ < 10) sendbyte(EOT);
    else
    {	sendbyte(EOT);  /* send 1st EOT */
	while ((readbyte(15) != ACK) && (attempts++ < RETRYMAX))
	   sendbyte(EOT);
	if (attempts >= RETRYMAX)
	   error("Remote System Not Responding on Completion", TRUE);
    }

    close(fd);
    restoremodes(FALSE);  
    sleep(5);  /* give other side time to return to terminal mode */
    if (LOGFLAG)
    {  fprintf(LOGFP, "\nSend Complete\n");
       fclose(LOGFP);
    }
    printf("\n");
    exit(0);

}

/*  print file size status information  */
prfilestat(name)
char *name;
{
	struct stat filestatbuf; /* file status info */

	stat(name, &filestatbuf);  /* get file status bytes */
	printf("\r\nUMODEM:  Estimated File Size %ldK, %ld Records, %ld Bytes",
	  (filestatbuf.st_size/1024)+1, (filestatbuf.st_size/128)+1,
	  filestatbuf.st_size);
	if (LOGFLAG)
	  fprintf(LOGFP,"Estimated File Size %ldK, %ld Records, %ld Bytes\n",
	  (filestatbuf.st_size/1024)+1, (filestatbuf.st_size/128)+1,
	  filestatbuf.st_size);
	return;
}

/* get a byte from data stream -- timeout if "seconds" elapses */
readbyte(seconds)
unsigned seconds;
{
	char c;
	
	signal(SIGALRM,alarmfunc);  /* catch alarms */	
	alarm(seconds);  /* set the alarm clock */
	if (read(0, &c, 1) < 0)  /* get a char; error means we timed out */
	  {
	     return(TIMEOUT);
	  }
	alarm(0);  /* turn off the alarm */
	return((c&BITMASK));  /* return the char */
}

/* send a byte to data stream */
sendbyte(data)
char data;
{
	char dataout;
	dataout = (data&BITMASK);  /* mask for 7 or 8 bits */
	write(1, &dataout, 1);  /* write the byte */
	return;
}

/* function for alarm clock timeouts */
alarmfunc()
{
	return;  /* this is basically a dummy function to force error */
		 /* status return on the "read" call in "readbyte"    */
}

/* print data on TTY setting */
ttyparams()
{
	
/*  Obtain TTY parameters for JHU UNIX  */
#ifdef JHU	
	gtty(0, &ttystemp);  /* get current tty params */
#endif

/*  Obtain TTY parameters for Version 7 UNIX  */
#ifdef VER7
	ioctl(0,TIOCGETP,&ttystemp);
#endif

	tty = ttyname(0);  /* get name of tty */
	stat(tty, &statbuf);  /* get more tty params */

	printf("\r\n\nTTY Device Parameter Display");
	  printf("\r\n\tTTY Device Name is %s\r\n\n", tty);
	  printf("\tAny Parity Allowed "); pryn(ANYP);
	  printf("\tEven Parity Allowed"); pryn(EVENP);
	  printf("\tOdd Parity Allowed "); pryn(ODDP);
	  printf("\tEcho Enabled       "); pryn(ECHO);
	  printf("\tLower Case Map     "); pryn(LCASE);
	  printf("\tTabs Expanded      "); pryn(XTABS);
	  printf("\tCR Mode Enabled    "); pryn(CRMOD);
	  printf("\tRAW Mode Enabled   "); pryn(RAW);

/*  Print extended terminal characteristics for JHU UNIX  */
#ifdef JHU
	  printf("\tBinary Mode Enabled"); pryn1(NB8);
	  printf("\tCR/LF in Col 72    "); pryn1(FOLD);
	  printf("\tRecognize ^S/^Q    "); pryn1(STALL);
	  printf("\tSend ^S/^Q         "); pryn1(TAPE);
	  printf("\tTerminal can BS    "); pryn1(SCOPE);
	  printf("\r\n");  /* New line to separate topics */
	  printf("\tTerminal Paging is "); pryn1(PAGE);
	    if (ttystemp.xflags&PAGE)
		printf("\t  Lines/Page is %d\r\n", ttystemp.xflags&PAGE);
	  printf("\r\n");  /* New line to separate topics */
	  printf("\tTTY Input Rate     :   ");
	    prbaud(ttystemp.ispeed);  /* print baud rate */
	  printf("\tTTY Output Rate    :   ");
	    prbaud(ttystemp.ospeed);  /* print output baud rate */
	  printf("\r\n");  /* New line to separate topics */
	  printf("\tMessages Enabled   ");
		if (statbuf.st_mode&011)
		   printf(":   Yes\r\n");
		else
		   printf(":   No\r\n");
#endif

/*  Print extended characteristics for Version 7 UNIX  */
#ifdef VER7
	  printf("\tTTY Input Rate     :   ");
	    prbaud(ttystemp.sg_ispeed);
	  printf("\tTTY Output Rate    :   ");
	    prbaud(ttystemp.sg_ospeed);  /* print output baud rate */
#endif

}

pryn(iflag)
int iflag;
{

/*  JHU UNIX flag test  */
#ifdef JHU
	if (ttystemp.mode&iflag)
#endif

/*  Version 7 UNIX flag test  */
#ifdef VER7
	if (ttystemp.sg_flags&iflag)
#endif

	   printf(":   Yes\r\n");
	else
	   printf(":   No\r\n");
}

/*  Extended flag test for JHU UNIX only  */
#ifdef JHU
pryn1(iflag)
int iflag;
{
	if (ttystemp.xflags&iflag)
	   printf(":   Yes\r\n");
	else
	   printf(":   No\r\n");
}
#endif

prbaud(speed)
char speed;
{
	switch (speed) {

/*  JHU UNIX speed flag cases  */
#ifdef JHU		
		case B0050 : printf("50"); break;
		case B0075 : printf("75"); break;
		case B0110 : printf("110"); break;
		case B0134 : printf("134.5"); break;
		case B0150 : printf("150"); break;
		case B0200 : printf("200"); break;
		case B0300 : printf("300"); break;
		case B0600 : printf("600"); break;
		case B1200 : printf("1200"); break;
		case B1800 : printf("1800"); break;
		case B2400 : printf("2400"); break;
		case B4800 : printf("4800"); break;
		case B9600 : printf("9600"); break;
		case EXT_A : printf("External A"); break;
		case EXT_B : printf("External B"); break;
#endif

/*  Version 7 UNIX speed flag cases  */
#ifdef VER7
		case B50 : printf("50"); break;
		case B75 : printf("75"); break;
		case B110 : printf("110"); break;
		case B134 : printf("134.5"); break;
		case B150 : printf("150"); break;
		case B200 : printf("200"); break;
		case B300 : printf("300"); break;
		case B600 : printf("600"); break;
		case B1200 : printf("1200"); break;
		case B1800 : printf("1800"); break;
		case B2400 : printf("2400"); break;
		case B4800 : printf("4800"); break;
		case B9600 : printf("9600"); break;
		case EXTA : printf("External A"); break;
		case EXTB : printf("External B"); break;
#endif

		default    : printf("Error"); break;
	}
	printf(" Baud\r\n");
}
