/***+
* Makesane(dev,path,ifl,ofl,cfl,lfl);
* MakeSane(dev,path);
*      int dev;   -- Unix file descriptor.
*     char*path;  -- File name for messages.
*      int ifl,ofl,cfl,lfl;
*
* Tell   the   device   driver   to  do  normal  I/O  on  the  device.
* Unfortunately, this is done in different ways on different brands of
* Unix.   Make  sure  that  your  system  is included somewhere in the
* following list.
*
* As with MakeRaw(), we have defined two macros, one  with  the  flags
* and one without. We normally use MakeSane(), and package the details
* for one system here.
*
* The biggest problem here is usually  discovering  the  tty  settings
* that  make csh behave sanely.  Most other shells don't much care, as
* long as the port is in a "cooked" mode with echoing on.
+***/
#include "V.h"
#include "sys_ioctl.h"
#include "ascii.h"
#if USE_termio
#	include "sys_termio.h"
#	include "fcntl.h"	/* This seems to be needed with termio */
global	TERM raw_trm;
global	TERM sav_trm;
global	Flag got_trm = 0;
#endif /*USE_termio*/
#if USE_termios			/* Tested on SunOS 4.1 (Solaris) */
#include "sys_termios.h"
global	TERM raw_trm;
global	TERM sav_trm;
global	Flag got_trm = 0;
#endif
#if USE_sgtty
#	include "fcntl.h"	/* Ultrix needs this, and it doesn't hurt on others */
#	include "sys_sgtty.h"
	int ldisc = NETLDISC;
global	TERM raw_trm;
global	TERM sav_trm;
#endif	/*USE_sgtty*/
extern int errno;
extern int zero;
extern Flag noxclfl;	/* This is in makeraw.c */
extern int baudcode;	/* CBAUD mask if baud code specified */
extern int baudrate;	/* CBAUD rate if baud rate specified */

makesane(dev,path,ifl,ofl,cfl,lfl)
	int dev;		/* File number */
	char *path;
	int  ifl;
	int  ofl;
	int  cfl;
	int  lfl;
{	int a, i=0;
	Fpush("makesane");
	V4 "Called with dev=%d `%s' i=0%o o=0%o c=0%o l=0%o",
		dev,path,ifl,ofl,cfl,lfl D;
	P2 "%s	Set file %d=\"%s\" to cooked mode.",Vtime(),dev,path D;
	errno = 0;
#if USE_termio && (defined(SYS5) || defined(Linux))
	i = Ioctl(dev,TCGETA,&raw_trm);
	if (Vlvl>3) {
		P1 "File %d:\tcflag=%06o [old]",dev,raw_trm.c_cflag D;
		P1 "File %d:\tiflag=%06o [old]",dev,raw_trm.c_iflag D;
		P1 "File %d:\tlflag=%06o [old]",dev,raw_trm.c_lflag D;
		P1 "File %d:\toflag=%06o [old]",dev,raw_trm.c_oflag D;
		P1 "File %d:\tcc=%02X %02X %02X %02X %02X %02X %02X %02X",dev,
			raw_trm.c_cc[0], raw_trm.c_cc[1], raw_trm.c_cc[2], raw_trm.c_cc[3],
			raw_trm.c_cc[4], raw_trm.c_cc[5], raw_trm.c_cc[6], raw_trm.c_cc[7] D;
	}
	V5 "Ioctl(dev=%d,TCGETA=%d,&raw_trm=%08X)=%d",dev,TCGETA,&raw_trm,i D;
	raw_trm.c_cflag &= CBAUD;	/* Save the speed */
	if (baudcode) {
		V4 "baudcode=0%o",baudcode D;
		raw_trm.c_cflag = baudcode;	/* Set a different speed */
	}
#ifdef Linux	/* Tested on a Linux 1.13 */
/*
*/
	raw_trm.c_cflag |= cfl? cfl: (CLOCAL | CREAD  | CS8);
	raw_trm.c_iflag  = ifl? ifl: (BRKINT | IGNPAR | ISTRIP | ICRNL | IXON | IXANY);
	raw_trm.c_lflag  = lfl? lfl: (ISIG   | ICANON | ECHO   | ECHOE | ECHOK);
	raw_trm.c_oflag  = ofl? ofl: (OPOST  | ONLCR  | TAB3);

	raw_trm.c_cc[0] = 0x03;	/* INTR = ^C */
	raw_trm.c_cc[1] = 0x1C;	/* QUIT = ^\ */
	raw_trm.c_cc[2] = 0x10;	/* ERASE= ^H [BS] */
	raw_trm.c_cc[3] = 0x18;	/* KILL = ^X */
	raw_trm.c_cc[4] = 0x04;	/* EOF  = ^D */
	raw_trm.c_cc[5] = 0;	/* EOL  char */
/*	raw_trm.c_cc[6] = __;	** Reserved */
	raw_trm.c_cc[7] = 0x1A;	/* SWTCH = ^Z */
#endif /* Linux */
#ifdef SYS5_2	/* Tested on a Motorola VME system */
	raw_trm.c_cflag |= HUPCL  | CREAD  | CS8;
	raw_trm.c_iflag  = BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
	raw_trm.c_lflag  = ISIG   | ICANON | ECHO   | ECHOE | ECHOK;
	raw_trm.c_oflag  = OPOST  | ONLCR  | TAB3;
	raw_trm.c_cc[0] = 0x03;	/* INTR = ^C */
	raw_trm.c_cc[1] = 0x1C;	/* QUIT = ^\ */
	raw_trm.c_cc[2] = 0x10;	/* ERASE= ^H [BS] */
	raw_trm.c_cc[3] = 0x18;	/* KILL = ^X */
	raw_trm.c_cc[4] = 0x04;	/* EOF  = ^D */
	raw_trm.c_cc[5] = 0;	/* EOL  char */
	raw_trm.c_cc[6] = 0;	/* EOL2 char */
/*	raw_trm.c_cc[7] = __;	** Reserved */
	raw_trm.c_cc[8] = 0x1A;	/* SUSP = ^Z */
	raw_trm.c_cc[9] = 0x19;	/* SUSP = ^Y */
#endif	/* SYS5_2 */
#ifdef SYS5_3	/* Tested on an ESIX 3.2 system */
	raw_trm.c_cflag |= CLOCAL | CREAD  | CS8;
	raw_trm.c_iflag  = BRKINT | IGNPAR | ISTRIP | ICRNL | IXON | IXANY;
	raw_trm.c_lflag  = ISIG   | ICANON | ECHO   | ECHOE | ECHOK;
	raw_trm.c_oflag  = OPOST  | ONLCR  | TAB3;
	raw_trm.c_cc[0] = 0x03;	/* INTR = ^C */
	raw_trm.c_cc[1] = 0x1C;	/* QUIT = ^\ */
	raw_trm.c_cc[2] = 0x10;	/* ERASE= ^H [BS] */
	raw_trm.c_cc[3] = 0x18;	/* KILL = ^X */
	raw_trm.c_cc[4] = 0x04;	/* EOF  = ^D */
	raw_trm.c_cc[5] = 0;	/* EOL  char */
/*	raw_trm.c_cc[6] = __;	** Reserved */
	raw_trm.c_cc[7] = 0x1A;	/* SWTCH = ^Z */
#endif /* SYS5_3 */
#ifdef SYS5_4	/* Tested on an ESIX 4.0.3 system */
/*
*/
	raw_trm.c_cflag |= cfl? cfl: (CLOCAL | CREAD  | CS8);
	raw_trm.c_iflag  = ifl? ifl: (BRKINT | IGNPAR | ISTRIP | ICRNL | IXON | IXANY);
	raw_trm.c_lflag  = lfl? lfl: (ISIG   | ICANON | ECHO   | ECHOE | ECHOK);
	raw_trm.c_oflag  = ofl? ofl: (OPOST  | ONLCR  | TAB3);

	raw_trm.c_cc[0] = 0x03;	/* INTR = ^C */
	raw_trm.c_cc[1] = 0x1C;	/* QUIT = ^\ */
	raw_trm.c_cc[2] = 0x10;	/* ERASE= ^H [BS] */
	raw_trm.c_cc[3] = 0x18;	/* KILL = ^X */
	raw_trm.c_cc[4] = 0x04;	/* EOF  = ^D */
	raw_trm.c_cc[5] = 0;	/* EOL  char */
/*	raw_trm.c_cc[6] = __;	** Reserved */
	raw_trm.c_cc[7] = 0x1A;	/* SWTCH = ^Z */
#endif /* SYS5_4 */
	if (Vlvl >= 4)	{
		P1 "File %d:\tcflag=%06o [new]",dev,raw_trm.c_cflag D;
		P1 "File %d:\tiflag=%06o [new]",dev,raw_trm.c_iflag D;
		P1 "File %d:\tlflag=%06o [new]",dev,raw_trm.c_lflag D;
		P1 "File %d:\toflag=%06o [new]",dev,raw_trm.c_oflag D;
		P1 "File %d:\tcc=%02X %02X %02X %02X %02X %02X %02X %02X",dev,
			raw_trm.c_cc[0], raw_trm.c_cc[1], raw_trm.c_cc[2], raw_trm.c_cc[3],
			raw_trm.c_cc[4], raw_trm.c_cc[5], raw_trm.c_cc[6], raw_trm.c_cc[7] D;
		Hexdnm(&raw_trm.c_cc[0],NCC,"c_cc:");
	}
	i = Ioctl(dev,TCSETA,&raw_trm);
/*
* One  of  the  more  disastrous  "improvements"  in  Sys/V   is   the
* introduction of an exclusive-use flag for serial ports.  Here is how
* we might  turn  this  misfeature  off  and  allow  multi-programming
* despite all their best efforts to prevent it.
*/
#ifdef TIONEXCL
	if (noxclfl) {	/* Tell the driver to turn off exclusive use */
		V2 "Disable exclusive use flag with TIOCNXCL..." D;
		V5 "before Ioctl(%d,TIOCNXCL=%d,%06lX)",dev,TIOCNXCL,&zero D;
		errno = 0;
		i = Ioctl(dev,TIOCNXCL,&zero);
		if (i < 0)
			P2 "%s\t### Can't use TIOCNXCL [Err %d=%s]",Vtime(),Erreason D;
		V5 " after Ioctl(%d,TIOCNXCL=%d,%06lX)=%d [Err %d=%s]",
			dev,TIOCNXCL,&zero,i,Erreason D;
	}
#endif	/*TIONEXCL*/
/*
* The following may be useful on other systems, to re-enable  blocking
* I/O if it has been set to non-blocking.  Some systems serial drivers
* work fine with blocking I/O, but some seem to  require  non-blocking
* to persuade a modem to accept incoming calls.
*/
	errno = 0;
	if ((a = fcntl(dev,F_GETFL,0)) >= 0) {
		V5 "fcntl(%d,F_GETFL,0)=%08X [Err %d=%s]",dev,a,Erreason D;
		a &= ~O_NDELAY;
		i = fcntl(dev,F_SETFL,a);
		V5 "fcntl(%d,F_SETFL,%08X)=%08X [Err %d=%s]",dev,a,i,Erreason D;
		P3 "%s	Set %s to blocking.",Vtime(),path D;
	}
#endif /* SYS5 */
#if USE_termio && defined(SYS3)	/* Tested only on PC/IX */
	V4 "Sys/3 sane mode." D;
	i = Ioctl(dev,TCGETA,&raw_trm);
	raw_trm.c_cflag |= HUPCL  | PARENB | CREAD  | CS7;
	raw_trm.c_iflag  = BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
	raw_trm.c_lflag  = ISIG   | ICANON | ECHO   | ECHOE | ECHOK;
	raw_trm.c_oflag  = OPOST  | ONLCR  | TAB3;
	V5 "cflag=%06o",raw_trm.c_cflag D;
	V5 "iflag=%06o",raw_trm.c_iflag D;
	V5 "lflag=%06o",raw_trm.c_lflag D;
	V5 "oflag=%06o",raw_trm.c_oflag D;
	i = Ioctl(dev,TCSETA,&raw_trm);
#endif /* SYS3 */
#if USE_sgtty && defined(BSV4_2)	/* Tested only on 4.2 */
	V4 "BSD sane mode." D;
	i = Ioctl(dev,TIOCSETD,&ldisc);
	i = Ioctl(dev,TIOCGETP,&raw_trm);
	raw_trm.sg_flags  =  SANE | TANDEM;
	i = Ioctl(dev,TIOCSETP,&raw_trm);
	if (errno) {
		V1 "Can't do sane i/o on \"%s\"",path V;
		Fail;
	}
#endif /* BSV4_2 */
#if USE_termios	/* Tested only on Sun Solaris */
#ifdef FREEBSD
	V4 "Using termios raw mode on FREEBSD." D;
	if (!got_trm) {
		V2 "Called with unknown sav_trm settings." D;
		Fail;
	}
	if ((i = tcsetattr(dev, TCSANOW, &sav_trm)) == -1) {
		V2 "Can't restore file %d from sav_trm [Err %d=%s=%s]",dev,Errinfo D;
		Fail;
	}
	V2 "File %d should be sane now.",dev D;
#else /* USE_termios && !FREEBSD */
/*
* Very similar to the S5R4 case above.
*/
	V4 "Solaris sane mode." D;
	i = Ioctl(dev,TCGETS,&raw_trm);

	if (Vlvl>3)	{
		P1 "File %d:\tcflag=%06o [old]",dev,raw_trm.c_cflag D;
		P1 "File %d:\tiflag=%06o [old]",dev,raw_trm.c_iflag D;
		P1 "File %d:\tlflag=%06o [old]",dev,raw_trm.c_lflag D;
		P1 "File %d:\toflag=%06o [old]",dev,raw_trm.c_oflag D;
		P1 "File %d:\tline =%02o=%02X [old]",dev,raw_trm.c_line,raw_trm.c_line D;
		P1 "File %d:\tcc=%02X %02X %02X %02X %02X %02X %02X %02X",dev,
			raw_trm.c_cc[0], raw_trm.c_cc[1], raw_trm.c_cc[2], raw_trm.c_cc[3],
			raw_trm.c_cc[4], raw_trm.c_cc[5], raw_trm.c_cc[6], raw_trm.c_cc[7] D;
		H5(&raw_trm.c_cc[0],NCCS,"c_cc:");
	}
	raw_trm.c_cflag &= B0;	/* Keep the speed */
	raw_trm.c_cflag |= cfl? cfl: (HUPCL  | CREAD  | CLOCAL | CS7);
	raw_trm.c_iflag  = ifl? ifl: (BRKINT | IGNPAR | ISTRIP | ICRNL | IXON  | IXANY);
	raw_trm.c_lflag  = lfl? lfl: (ISIG   | ICANON | IEXTEN | ECHO  | ECHOE | ECHOK);
	raw_trm.c_oflag  = ofl? ofl: (OPOST  | ONLCR  | TAB3);
/*
* Here are the c_cc settings from the termio man page:
*/
	raw_trm.c_cc[ 0] = ASC_ETX;	/* VINTR */
	raw_trm.c_cc[ 1] = ASC_FS;	/* VQUIT */
	raw_trm.c_cc[ 2] = ASC_DEL;	/* VERASE */
	raw_trm.c_cc[ 3] = ASC_NAK;	/* VKILL */
	raw_trm.c_cc[ 4] = ASC_EOT;	/* VEOF */
	raw_trm.c_cc[ 5] = ASC_NUL;	/* VEOL */
	raw_trm.c_cc[ 6] = ASC_NUL;	/* VEOL2 */
	raw_trm.c_cc[ 7] = ASC_NUL;	/* VSWTCH */
	raw_trm.c_cc[ 8] = ASC_DC1;	/* VSTART */
	raw_trm.c_cc[ 9] = ASC_DC3;	/* VSTOP */
	raw_trm.c_cc[10] = ASC_EM;	/* VSUSP */
	raw_trm.c_cc[12] = ASC_DC2;	/* VREPRINT */
	raw_trm.c_cc[13] = ASC_SI;	/* VDISCARD */
	raw_trm.c_cc[14] = ASC_ETB;	/* VWERASE */
	raw_trm.c_cc[15] = ASC_SYN;	/* VLNEXT */
	if (Vlvl >= 4)	{
		P1 "File %d:\tcflag=%06o [new]",dev,raw_trm.c_cflag D;
		P1 "File %d:\tiflag=%06o [new]",dev,raw_trm.c_iflag D;
		P1 "File %d:\tlflag=%06o [new]",dev,raw_trm.c_lflag D;
		P1 "File %d:\toflag=%06o [new]",dev,raw_trm.c_oflag D;
		P1 "File %d:\tline =%02o=%02X [new]",dev,raw_trm.c_line,raw_trm.c_line D;
		P1 "File %d:\tcc=%02X %02X %02X %02X %02X %02X %02X %02X",dev,
			raw_trm.c_cc[0], raw_trm.c_cc[1], raw_trm.c_cc[2], raw_trm.c_cc[3],
			raw_trm.c_cc[4], raw_trm.c_cc[5], raw_trm.c_cc[6], raw_trm.c_cc[7] D;
		H5(&raw_trm.c_cc[0],NCCS,"c_cc:");
	}
	if ((i = Ioctl(dev,TCSETS,&raw_trm)) < 0) {
		V1 "Can't do sane i/o on \"%s\" [Err %d=%s]",path,Erreason V;
		Fail;
	}
#endif /* USE_termios && !FREEBSD */
#endif /* USE_termios */

#if USE_sgtty && defined(SUN4)
	V4 "SUN4 sane mode." D;
	i = Ioctl(dev,TIOCSETD,&ldisc);
	i = Ioctl(dev,TIOCGETP,&raw_trm);
	raw_trm.sg_flags  =  CRTBS | CRTERA | CRTKIL | PASS8 | TANDEM;
	if ((i = Ioctl(dev,TIOCSETP,&raw_trm)) < 0) {
		V1 "Can't do sane i/o on \"%s\" [Err %d=%s]",path,Erreason V;
		Fail;
	}
#endif /* SUN4 */

#if USE_sgtty && defined(ULTRIX)	/* Tested on Ultrix 3.[012] */
	V4 "Ultrix sane mode." D;
#ifdef VAX
	V5 "VAX/Ultrix sane mode." D;
#endif	/* VAX */
#ifdef PMAX
	V5 "PMAX/Ultrix sane mode." D;
#endif	/* PMAX */
	ldisc = NTTYDISC;
	V3 "Set file %d=%s to line discipline %d=NTTY",dev,path,ldisc D;
	i = Ioctl(dev,TIOCSETD,&ldisc);

	Ioctl(dev,TIOCGETP,&raw_trm);
	raw_trm.sg_flags  = ECHO | NL2 | CRMOD | TANDEM;
	raw_trm.sg_ispeed =
	raw_trm.sg_ospeed = baudcode;
	V4 "Set raw_trm flags=0%o ispeed=ospeed=%d.",
		raw_trm.sg_flags,raw_trm.sg_ispeed D;
	i = Ioctl(dev,TIOCSETP,&raw_trm);
	if (errno) {
		V1 "Can't do sane i/o on \"%s\"",path V;
		Fail;
	}
	if (noxclfl) {	/* Tell the driver to turn off exclusive use */
		V2 "Disable exclusive use flag with TIOCNXCL..." D;
		V5 "before Ioctl(%d,TIOCNXCL=%d,%06lX)",dev,TIOCNXCL,&zero D;
		errno = 0;
		i = Ioctl(dev,TIOCNXCL,&zero);
		if (i < 0)
			P2 "%s\t### Can't use TIOCNXCL [Err %d=%s]",Vtime(),Erreason D;
		V5 " after Ioctl(%d,TIOCNXCL=%d,%06lX)=%d [Err %d=%s]",
			dev,TIOCNXCL,&zero,i,Erreason D;
	}
	if ((i = fcntl(dev,F_GETFL,0)) > 0) {
		P2 "%s	Set %s to blocking.",Vtime(),path D;
		fcntl(dev,F_SETFL,i & ~FNBLOCK);
	} else
		P2 "%s	### fcntl(%d,F_GETFL)=%d [Err %d=%s]",Vtime(),dev,i,Erreason D;
#endif	/* ULTRIX */

#if USE_termio && defined(POSIX)
	V4 "POSIX sane mode." D;
	i = Ioctl(dev,TCGETA,&raw_trm);
	if (Vlvl >= 4) {
	  P4 "%d:\tcflag=%06o [old]",dev,raw_trm.c_cflag D;
	  P4 "%d:\tiflag=%06o [old]",dev,raw_trm.c_iflag D;
	  P4 "%d:\tlflag=%06o [old]",dev,raw_trm.c_lflag D;
	  P4 "%d:\toflag=%06o [old]",dev,raw_trm.c_oflag D;
	}
	V5 "Ioctl(dev=%d,TCGETA=%d,&raw_trm=%08X)=%d",dev,TCGETA,&raw_trm,i D;
	raw_trm.c_cflag &= CBAUD;	/* Save the speed */
	if (baudcode) {
	  V4 "baudcode=0%o",baudcode D;
	  raw_trm.c_cflag = baudcode;	/* Set a different speed */
	}
	raw_trm.c_cflag |= HUPCL  | CREAD  | CS8;
	raw_trm.c_iflag  = BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
	raw_trm.c_lflag  = ISIG   | ICANON | ECHO   | ECHOE | ECHOK;
	raw_trm.c_oflag  = OPOST  | ONLCR  | TAB3;
	raw_trm.c_cc[0] = 0x03;	/* INTR = ^C */
	raw_trm.c_cc[1] = 0x1C;	/* QUIT = ^\ */
	raw_trm.c_cc[2] = 0x10;	/* ERASE= ^H [BS] */
	raw_trm.c_cc[3] = 0x18;	/* KILL = ^X */
	raw_trm.c_cc[4] = 0x04;	/* EOF  = ^D */
	raw_trm.c_cc[5] = 0;	/* EOL  char */
	raw_trm.c_cc[6] = 0;	/* EOL2 char */
/*	raw_trm.c_cc[7] = __;	** Reserved */
	raw_trm.c_cc[8] = 0x1A;	/* SUSP = ^Z */
	raw_trm.c_cc[9] = 0x19;	/* SUSP = ^Y */
	if (Vlvl >= 5)  {
	  P5 "%d:\tcflag=%06o [new]",dev,raw_trm.c_cflag D;
	  P5 "%d:\tiflag=%06o [new]",dev,raw_trm.c_iflag D;
	  P5 "%d:\tlflag=%06o [new]",dev,raw_trm.c_lflag D;
	  P5 "%d:\toflag=%06o [new]",dev,raw_trm.c_oflag D;
	  Hexdnm(&raw_trm.c_cc[0],NCC,"c_cc:");
	}
	i = Ioctl(dev,TCSETA,&raw_trm);
#endif /* POSIX */
	V5 "%d is now sane.",dev D;
fail:
	Fpop;
	return i;
}
