/*
 * Rev 5-09-89 This file contains Unix specific code for setting terminal modes,
 * very little is specific to ZMODEM or YMODEM per se (that code is in sz.c
 * and rz.c).  The CRC-16 routines used by XMODEM, YMODEM, and ZMODEM are
 * also in this file, a fast table driven macro version
 *
 * V7/BSD HACKERS:	SEE NOTES UNDER mode(2) !!!
 *
 * This file is #included so the main file can set parameters such as HOWMANY.
 * See the main files (rz.c/sz.c) for compile instructions. */

#ifdef V7
#	include <sys/types.h>
#	include <sys/stat.h>
#	define STAT
#	include <sgtty.h>
#	define OS "V7/BSD"
#	define ROPMODE "r"
#	ifdef LLITOUT
long		Locmode;			/* Saved "local mode" for 4.x BSD "new
								 * driver" */
long		Locbit = LLITOUT;	/* Bit SUPPOSED to disable output
								 * translations */
#		include <strings.h>
#	endif
#endif

#ifdef	__TOWNS__
#	define OS "TownsOS"
#	define ROPMODE "rb"
#	define MODE2OK
#else
#	ifndef OS
#		ifndef USG
#			define USG
#		endif
#	endif

#	ifdef USG
#		include <sys/types.h>
#		include <sys/stat.h>
#		define STAT
#		define OS "SYS III/V"
#		define ROPMODE "r"
#		define MODE2OK
#		include <string.h>
#	endif
#endif

#ifdef T6K
#	include <sys/ioctl.h>		/* JPRadley: for the Tandy 6000 */
#endif

#if HOWMANY  > 255
Howmany must be 255 or less
#endif

/*
 * return 1 iff stdout and stderr are different devices indicating this
 * program operating with a modem on a different line
 */
int 		Fromcu; 			/* Were called from cu or yam */

void	from_cu(void)
{
#ifdef	__TOWNS__
	Fromcu = 1;
#else
	struct stat a, b;

	fstat(1, &a);
	fstat(2, &b);
	Fromcu = a.st_rdev != b.st_rdev;
	return;
#endif
}

void	cucheck(void)
{
	if ( Fromcu )
		USR_fprintf(stderr, "Please read the manual page BUGS chapter!\r\n");
}


#ifndef	__TOWNS__
struct
{
	unsigned	baudr;
	int 		speedcode;
} speeds[] =
{
	  110, B110,
	  300, B300,
	  600, B600,
	 1200, B1200,
	 2400, B2400,
	 4800, B4800,
	 9600, B9600,
	19200, EXTA,
	38400, EXTB,
	0,
};
#endif

int 		Twostop;			/* Use two stop bits */

/*
 * The following uses an external rdchk() routine if available, otherwise
 * defines the function for BSD or fakes it for SYSV.
 */

#ifdef	__TOWNS__
#	ifdef	READCHECK
	int		rdchk(int f)
	{
		if ( RS_chk(RsPort) > 0 )
			return (1);
		else
			return (0);
	}
#	endif
#endif

#ifndef READCHECK
#ifdef FIONREAD
#define READCHECK
/*
 * Return non 0 iff something to read from io descriptor f
 */
rdchk(f)
{
	static long lf;

	ioctl(f, FIONREAD, &lf);
	return ((int) lf);
}

#else							/* FIONREAD */

#ifdef SV
#define READCHECK
#include <fcntl.h>

int 		checked = 0;
/*
 * Nonblocking I/O is a bit different in System V, Release 2 Note: this rdchk
 * vsn throws away a byte, OK for ZMODEM sender because protocol design
 * anticipates this problem.
 */
#define EATSIT
rdchk(f)
{
	int 		lf, savestat;
	static char bchecked;

	savestat = fcntl(f, F_GETFL);
	fcntl(f, F_SETFL, savestat | O_NDELAY);
	lf = read(f, &bchecked, 1);
	fcntl(f, F_SETFL, savestat);
	checked = bchecked & 0377;	/* force unsigned byte */
	return (lf);
}
#endif
#endif
#endif


static unsigned	getspeed(int code)
{
#ifdef	__TOWNS__
	switch ( code )
	{
		case RSBAUD_300:	return (300);
		case RSBAUD_600:	return (600);
		case RSBAUD_1200:	return (1200);
		case RSBAUD_2400:	return (2400);
		case RSBAUD_4800:	return (4800);
		case RSBAUD_9600:	return (9600);
		case RSBAUD_19200:	return (19200);
		case RSBAUD_38400:	return (38400);
		default:			return (9600);
	}
#else
	register int	n;

	for ( n = 0; speeds[n].baudr; ++n )
		if (speeds[n].speedcode == code)
			return speeds[n].baudr;
	return 38400;				/* Assume fifo if ioctl failed */
#endif
}


#ifdef	__TOWNS__
	static int	OldMd, OldBaud;
	static int	SavePort = -1;
#else
#ifdef ICANON
struct termio oldtty, tty;
#else
struct sgttyb oldtty, tty;
struct tchars oldtch, tch;
#endif
#endif

#ifdef	__TOWNS__
static	int		rsOpenChk(int port)
{
	extern	int		RsBaud;
	extern	int		RsMd;

	if ( RsCtrl[port].use == 0 )
	{
		RS_open( port );
		if ( RsBaud >= 0 || RsMd >= 0 )
		{
			int		baud;
			int		md;

			baud = ( RsBaud >= 0 ) ? RsBaud : RsCtrl[RsPort].rsb->baud;
			md   = ( RsMd >= 0 ) ? RsMd : RsCtrl[RsPort].rsb->mode;
			RS_reopen(RsPort, md, baud );
		}
	}
	return (1); /* OK */
}
#endif

/*
 * mode(n)
 *   3: save old tty stat, set raw mode with flow control
 *   2: set XON/XOFF for sb/sz with ZMODEM or YMODEM-g
 *   1: save old tty stat, set raw mode
 *   0: restore original tty mode
 */
static	int		mode(int n)
{
	static int	did0 = FALSE;

	vfile("mode:%d", n);
	switch (n)
	{
#ifdef	__TOWNS__
		case 2:			/* set XON/XOFF for sb/sz with ZMODEM or YMODEM-g */
			if (!did0)
			{
				if ( rsOpenChk( RsPort ) )
				{
					OldMd   = RsCtrl[RsPort].rsb->mode;
					OldBaud = RsCtrl[RsPort].rsb->baud;
				} else
					return ERROR;
			}
			{
				int		md, baud;
				md   = OldMd | RSMD_XCTRL_ON;
				RS_reopen(RsPort, md, OldBaud);	/* XCTRL ON */
			}
			RSB_CTRL(RsPort,0x22);
			did0 = TRUE;
			return OK;

		case 1:	/* save old tty stat, set raw mode */
		case 3:	/* save old tty stat, set raw mode with flow control */
			if (!did0)
			{	/* save */
				if ( rsOpenChk( RsPort ) )
				{
					OldMd   = RsCtrl[RsPort].rsb->mode;
					OldBaud = RsCtrl[RsPort].rsb->baud;
				} else
					return ERROR;
			}
			{
				int		md, baud;

				md   = OldMd & (~RSMD_XCTRL_ON);	/* XCTRL OFF */
				RS_reopen(RsPort, md, OldBaud);
				RSB_CTRL(RsPort,0x22);
			}
			/*Effbaud =*/ Baudrate = getspeed(OldBaud);
			did0 = TRUE;
			return OK;

		case 0: /* restore original tty mode */
			if (!did0)
				return ERROR;
			RS_reopen(RsPort, OldMd, OldBaud);
			did0 = FALSE;
			return OK;

		default:
			return ERROR;
#else
#ifdef USG
		case 2: 		/* Un-raw mode used by sz, sb when -g
								 * detected */
			if (!did0)
				(void) ioctl(0, TCGETA, &oldtty);
			tty = oldtty;

			tty.c_iflag = BRKINT | IXON;

			tty.c_oflag = 0;	/* Transparent output */

			tty.c_cflag &= ~PARENB; 	/* Disable parity */
			tty.c_cflag |= CS8; /* Set character size = 8 */
			if (Twostop)
				tty.c_cflag |= CSTOPB;	/* Set two stop bits */


#ifdef READCHECK
			tty.c_lflag = Zmodem ? 0 : ISIG;
			tty.c_cc[VINTR] = Zmodem ? -1 : 030;		/* Interrupt char */
#else
			tty.c_lflag = ISIG;
			tty.c_cc[VINTR] = Zmodem ? 03 : 030;		/* Interrupt char */
#endif
			tty.c_cc[VQUIT] = -1;		/* Quit char */
#ifdef NFGVMIN
			tty.c_cc[VMIN] = 1;
#else
			tty.c_cc[VMIN] = 3; /* This many chars satisfies reads */
#endif
			tty.c_cc[VTIME] = 1;/* or in this many tenths of seconds */

			(void) ioctl(0, TCSETAW, &tty);
			did0 = TRUE;
			return OK;
		case 1:
		case 3:
			if (!did0)
				(void) ioctl(0, TCGETA, &oldtty);
			tty = oldtty;

			tty.c_iflag = n == 3 ? (IGNBRK | IXOFF) : IGNBRK;

			/* No echo, crlf mapping, INTR, QUIT, delays, no erase/kill */
			tty.c_lflag &= ~(ECHO | ICANON | ISIG);

			tty.c_oflag = 0;	/* Transparent output */

			tty.c_cflag &= ~PARENB; 	/* Same baud rate, disable parity */
			tty.c_cflag |= CS8; /* Set character size = 8 */
			if (Twostop)
				tty.c_cflag |= CSTOPB;	/* Set two stop bits */
#ifdef NFGVMIN
			tty.c_cc[VMIN] = 1; /* This many chars satisfies reads */
#else
			tty.c_cc[VMIN] = HOWMANY;	/* This many chars satisfies reads */
#endif
			tty.c_cc[VTIME] = 1;/* or in this many tenths of seconds */
			(void) ioctl(0, TCSETAW, &tty);
			did0 = TRUE;
			Effbaud = Baudrate = getspeed(tty.c_cflag & CBAUD);
			return OK;
#endif

#ifdef V7
			/*
			 * NOTE: this should transmit all 8 bits and at the same time
			 * respond to XOFF/XON flow control.  If no FIONREAD or other
			 * rdchk() alternative, also must respond to INTRRUPT char This
			 * doesn't work with V7.  It should work with LLITOUT, but
			 * LLITOUT was broken on the machine I tried it on.
			 */
		case 2: 		/* Un-raw mode used by sz, sb when -g
								 * detected */
			if (!did0)
			{
				ioctl(0, TIOCEXCL, 0);
				ioctl(0, TIOCGETP, &oldtty);
				ioctl(0, TIOCGETC, &oldtch);
#ifdef LLITOUT
				ioctl(0, TIOCLGET, &Locmode);
#endif
			}
			tty = oldtty;
			tch = oldtch;
#ifdef READCHECK
			tch.t_intrc = Zmodem ? -1 : 030;	/* Interrupt char */
#else
			tch.t_intrc = Zmodem ? 03 : 030;	/* Interrupt char */
#endif
			tty.sg_flags |= (ODDP | EVENP | CBREAK);
			tty.sg_flags &= ~(ALLDELAY | CRMOD | ECHO | LCASE);
			ioctl(0, TIOCSETP, &tty);
			ioctl(0, TIOCSETC, &tch);
#ifdef LLITOUT
			ioctl(0, TIOCLBIS, &Locbit);
#endif
			bibi(99);			/* un-raw doesn't work w/o lit out */
			did0 = TRUE;
			return OK;
		case 1:
		case 3:
			if (!did0)
			{
				ioctl(0, TIOCEXCL, 0);
				ioctl(0, TIOCGETP, &oldtty);
				ioctl(0, TIOCGETC, &oldtch);
#ifdef LLITOUT
				ioctl(0, TIOCLGET, &Locmode);
#endif
			}
			tty = oldtty;
			tty.sg_flags |= (RAW | TANDEM);
			tty.sg_flags &= ~ECHO;
			ioctl(0, TIOCSETP, &tty);
			did0 = TRUE;
			Effbaud = Baudrate = getspeed(tty.sg_ospeed);
			return OK;
#endif
		case 0:
			if (!did0)
				return ERROR;
#ifdef USG
			(void) ioctl(0, TCSBRK, 1); /* Wait for output to drain */
			(void) ioctl(0, TCFLSH, 1); /* Flush input queue */
			(void) ioctl(0, TCSETAW, &oldtty);	/* Restore modes */
			(void) ioctl(0, TCXONC, 1); /* Restart output */
#endif
#ifdef V7
			ioctl(0, TIOCSETP, &oldtty);
			ioctl(0, TIOCSETC, &oldtch);
			ioctl(0, TIOCNXCL, 0);
#ifdef LLITOUT
			ioctl(0, TIOCLSET, &Locmode);
#endif
#endif

			return OK;
		default:
			return ERROR;
#endif	/* endof ifdef __TOWNS__ */
	}
}

void	sendbrk(void)
{
#ifdef	__TOWNS__
	RS_breakOut( RsPort, 50 );
#else
#ifdef V7
#ifdef TIOCSBRK
#define CANBREAK
	sleep(1);
	ioctl(0, TIOCSBRK, 0);
	sleep(1);
	ioctl(0, TIOCCBRK, 0);
#endif
#endif
#ifdef USG
#define CANBREAK
	ioctl(0, TCSBRK, 0);
#endif
#endif
}

/* End of rbsb.c */
