/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/

#ident	"@(#)line.c	1.8	95/01/09 SMI"	/* SVr4.0 1.2	*/

/* This is a new line.c, which consists of line.c and culine.c
 * merged together.
 */

#include "uucp.h"
#include <rpc/trace.h> 

static const struct sg_spds {
	int	sp_val,
		sp_name;
} spds[] = {
	{  50,   B50},
	{  75,   B75},
	{ 110,  B110},
	{ 134,  B134},
	{ 150,  B150},
	{ 200,  B200},
	{ 300,  B300},
	{ 600,  B600},
	{1200, B1200},
	{1800, B1800},
	{2400, B2400},
	{4800, B4800},
	{9600, B9600},
#ifdef EXTA
	{19200,	EXTA},
#endif
#ifdef B19200
	{19200,	B19200},
#endif
#ifdef B38400
	{38400,	B38400},
#endif
	{57600, B57600},
	{76800, B76800},
	{115200, B115200},
	{153600, B153600},
	{230400, B230400},
	{307200, B307200},
	{460800, B460800},
	{0,    0}
};

#define PACKSIZE	64
#define HEADERSIZE	6

GLOBAL int
     packsize = PACKSIZE,
    xpacksize = PACKSIZE;

#define SNDFILE	'S'
#define RCVFILE 'R'
#define RESET	'X'

GLOBAL int donap;	/* for speedup hook in pk1.c */
static int Saved_line;		/* was savline() successful?	*/
static int Saved_termios;	/* was termios saved?	*/
GLOBAL int
	Oddflag,	/* Default is no parity */
	Evenflag,	/* Default is no parity */
	Duplex = 1,	/* Default is full duplex */
	Terminal,	/* Default is no terminal */
	term_8bit = -1,	/* Default to terminal setting or 8 bit */
	line_8bit = -1;	/* Default is same as terminal */

static const char P_PARITY[] = "Parity option error\r\n";

#ifdef ATTSV

static struct termio Savettyb;
static struct termios Savettybs;
/*
 * set speed/echo/mode...
 *	tty 	-> terminal name
 *	spwant 	-> speed
 *	type	-> type
 *
 *	if spwant == 0, speed is untouched
 *	type is unused, but needed for compatibility
 *
 * return:  
 *	none
 */
/*ARGSUSED*/
GLOBAL void
fixline(tty, spwant, type)
int	tty, spwant, type;
{
	register const struct sg_spds	*ps;
	struct termio		ttbuf;
	struct termios		ttbufs;
	int			speed = -1;
	int			i, istermios, ospeed;

	trace4(TR_fixline, 0, tty, spwant, type);
	DEBUG(6, "fixline(%d, ", tty);
	DEBUG(6, "%d)\n", spwant);
	if ((istermios = (*Ioctl)(tty, TCGETS, &ttbufs)) < 0) {
	    if ((*Ioctl)(tty, TCGETA, &ttbuf) != 0) {
		trace1(TR_fixline, 1);
		return;
	    } else {
		ttbufs.c_lflag = ttbuf.c_lflag;
		ttbufs.c_oflag = ttbuf.c_oflag;
		ttbufs.c_iflag = ttbuf.c_iflag;
		ttbufs.c_cflag = ttbuf.c_cflag;
		for(i = 0; i < NCC; i++)
			ttbufs.c_cc[i] = ttbuf.c_cc[i];
	    }
	}
	if (spwant > 0) {
		for (ps = spds; ps->sp_val; ps++)
			if (ps->sp_val == spwant) {
				speed = ps->sp_name;
				break;
			}
		if (speed < 0) {
			/*EMPTY*/
		    DEBUG(5, "speed (%d) not supported\n", spwant);
		}
		ASSERT(speed >= 0, "BAD SPEED", "", spwant);
		ttbufs.c_cflag &= 0xffff0000;
		cfsetospeed(&ttbufs, speed);
	} else { /* determine the current speed setting */
		ospeed = cfgetospeed(&ttbufs);
		ttbufs.c_cflag &= 0xffff0000;
		cfsetospeed(&ttbufs, ospeed);
		for (ps = spds; ps->sp_val; ps++)
			if (ps->sp_name == ospeed) {
				spwant = ps->sp_val;
				break;
			}
	}
	ttbufs.c_iflag &= 0xffff0000;
	ttbufs.c_oflag &= 0xffff0000;
	ttbufs.c_lflag &= 0xffff0000;

#ifdef NO_MODEM_CTRL
	/*   CLOCAL may cause problems on pdp11s with DHs */
	if (type == D_DIRECT) {
		DEBUG(4, "fixline - direct\n%s", "");
		ttbufs.c_cflag |= CLOCAL;
	} else
#endif /* NO_MODEM_CTRL */
		ttbufs.c_cflag &= ~CLOCAL;

	if (EQUALS(Progname, "cu")) {

		/* set attributes associated with -h, -t, -e, and -o options */

		ttbufs.c_iflag = (IGNPAR | IGNBRK | IXON | IXOFF);
		if (line_8bit) {
		    ttbufs.c_cflag |= CS8;
		    ttbufs.c_iflag &= ~ISTRIP;
		} else {
		    ttbufs.c_cflag |= CS7;
		    ttbufs.c_iflag |= ISTRIP;
		}

		ttbufs.c_cc[VEOF] = '\1';
		ttbufs.c_cflag |= (CREAD | (speed ? HUPCL : 0));

		if (Evenflag) {				/*even parity -e */
		    if (ttbufs.c_cflag & PARENB) {
				VERBOSE(P_PARITY, 0);
				trace1(TR_fixline, 1);
				exit (1);
		    } else 
				ttbufs.c_cflag |= PARENB;
		} else if (Oddflag) {			/*odd parity -o */
		    if (ttbufs.c_cflag & PARENB) {
				VERBOSE(P_PARITY, 0);
				trace1(TR_fixline, 1);
				exit (1);
		    } else {
				ttbufs.c_cflag |= PARODD;
				ttbufs.c_cflag |= PARENB;
		    }
		}

		if (!Duplex)				/*half duplex -h */
		    ttbufs.c_iflag &= ~(IXON | IXOFF);
		if (Terminal)				/* -t */
		    ttbufs.c_oflag |= (OPOST | ONLCR);

	} else { /* non-cu */
		ttbufs.c_cflag |= (CS8 | CREAD | (speed ? HUPCL : 0));
		ttbufs.c_cc[VMIN] = HEADERSIZE;
		ttbufs.c_cc[VTIME] = 1;
	}

	donap = (spwant > 0 && spwant < 4800);

	if (istermios < 0) {
		ttbuf.c_lflag = ttbufs.c_lflag;
		ttbuf.c_oflag = ttbufs.c_oflag;
		ttbuf.c_iflag = ttbufs.c_iflag;
		ttbuf.c_cflag = ttbufs.c_cflag;
		for(i = 0; i < NCC; i++)
			ttbuf.c_cc[i] = ttbufs.c_cc[i];
		ASSERT((*Ioctl)(tty, TCSETAW, &ttbuf) >= 0,
			"RETURN FROM fixline ioctl", "", errno);
	} else {
		ASSERT((*Ioctl)(tty, TCSETSW, &ttbufs) >= 0,
			"RETURN FROM fixline ioctl", "", errno);
	}
	trace1(TR_fixline, 1);
	return;
}

GLOBAL void
sethup(dcf)
int	dcf;
{
	struct termio ttbuf;

	trace2(TR_sethup, 0, dcf);
	if ((*Ioctl)(dcf, TCGETA, &ttbuf) != 0) {
		trace1(TR_sethup, 1);
		return;
	}
	if (!(ttbuf.c_cflag & HUPCL)) {
		ttbuf.c_cflag |= HUPCL;
		(void) (*Ioctl)(dcf, TCSETAW, &ttbuf);
	}
	trace1(TR_sethup, 1);
	return;
}

GLOBAL void
ttygenbrk(fn)
register int	fn;
{
	trace2(TR_ttygenbrk, 0, fn);
	if (isatty(fn)) 
		(void) (*Ioctl)(fn, TCSBRK, 0);
	trace1(TR_ttygenbrk, 1);
	return;
}


#ifndef DIAL
/*
 * optimize line setting for sending or receiving files
 * return:
 *	none
 */
GLOBAL void
setline(type)
register char	type;
{
	static struct termio tbuf;
	static struct termios tbufs;
	int i, vtime, istermios, ospeed;
	
	trace1(TR_setline, 0);
	DEBUG(2, "setline - %c\n", type);
	if ((istermios = (*Ioctl)(Ifn, TCGETS, &tbufs)) < 0) {
		if ((*Ioctl)(Ifn, TCGETA, &tbuf) != 0) {
			trace1(TR_setline, 1);
			return;
		} else {
			tbufs.c_lflag = tbuf.c_lflag;
			tbufs.c_oflag = tbuf.c_oflag;
			tbufs.c_iflag = tbuf.c_iflag;
			tbufs.c_cflag = tbuf.c_cflag;
			for(i = 0; i < NCC; i++)
				tbufs.c_cc[i] = tbuf.c_cc[i];
		}
	}
	switch (type) {
	case RCVFILE:
		ospeed = cfgetospeed(&tbufs);
		switch (ospeed) {
		case B9600:
			vtime = 1;
			break;
		case B4800:
			vtime = 4;
			break;
		default:
			vtime = 8;
			break;
		}
		if (tbufs.c_cc[VMIN] != packsize ||
		    tbufs.c_cc[VTIME] != vtime) {
		    tbufs.c_cc[VMIN] = packsize;
		    tbufs.c_cc[VTIME] = vtime;
		    if (istermios < 0) {
			tbuf.c_lflag = tbufs.c_lflag;
			tbuf.c_oflag = tbufs.c_oflag;
			tbuf.c_iflag = tbufs.c_iflag;
			tbuf.c_cflag = tbufs.c_cflag;
			for(i = 0; i < NCC; i++)
				tbuf.c_cc[i] = tbufs.c_cc[i];
			if ((*Ioctl)(Ifn, TCSETAW, &tbuf) != 0)
				DEBUG(4, "setline Ioctl failed errno=%d\n", errno);
		    } else {
			if ((*Ioctl)(Ifn, TCSETSW, &tbufs) != 0)
				DEBUG(4, "setline Ioctl failed errno=%d\n", errno);
		    }
		}
		break;

	case SNDFILE:
	case RESET:
		if (tbufs.c_cc[VMIN] != HEADERSIZE) {
		    tbufs.c_cc[VMIN] = HEADERSIZE;
		    if (istermios < 0) {
			tbuf.c_lflag = tbufs.c_lflag;
			tbuf.c_oflag = tbufs.c_oflag;
			tbuf.c_iflag = tbufs.c_iflag;
			tbuf.c_cflag = tbufs.c_cflag;
			for(i = 0; i < NCC; i++)
				tbuf.c_cc[i] = tbufs.c_cc[i];
			if ((*Ioctl)(Ifn, TCSETAW, &tbuf) != 0)
				DEBUG(4, "setline Ioctl failed errno=%d\n", errno);
		    } else {
			if ((*Ioctl)(Ifn, TCSETSW, &tbufs) != 0)
				DEBUG(4, "setline Ioctl failed errno=%d\n", errno);
		    }
		}
		break;
	}
	trace1(TR_setline, 1);
	return;
}
#endif

GLOBAL int
savline()
{
	trace1(TR_savline, 0);
	if ((Saved_termios = (*Ioctl)(0, TCGETS, &Savettybs)) < 0) {
	    if ((*Ioctl)(0, TCGETA, &Savettyb) != 0) {
		Saved_line = FALSE;
	    } else {
		Saved_line = TRUE;
		Savettyb.c_cflag = (Savettyb.c_cflag & ~CS8) | CS7;
		Savettyb.c_oflag |= OPOST;
		Savettyb.c_lflag |= (ISIG|ICANON|ECHO);
	    }
	} else {
		Saved_line = TRUE;
		Savettybs.c_cflag = (Savettybs.c_cflag & ~CS8) | CS7;
		Savettybs.c_oflag |= OPOST;
		Savettybs.c_lflag |= (ISIG|ICANON|ECHO);
	}
	trace1(TR_savline, 1);
	return (0);
}

#ifdef SYTEK

/*
 *	sytfixline(tty, spwant)	set speed/echo/mode...
 *	int tty, spwant;
 *
 *	return codes:  none
 */

GLOBAL void
sytfixline(tty, spwant)
int tty, spwant;
{
	struct termio ttbuf;
	struct termios ttbufs;
	const struct sg_spds *ps;
	int speed = -1;
	int i, ret, istermios;

	trace3(TR_sytfixline, 0, tty, spwant);
	if ((istermios = (*Ioctl)(tty, TCGETS, &ttbufs)) < 0) {
		if ((*Ioctl)(tty, TCGETA, &ttbuf) != 0) {
			trace1(TR_sytfixline, 1);
			return;
		} else {
			ttbufs.c_lflag = ttbuf.c_lflag;
			ttbufs.c_oflag = ttbuf.c_oflag;
			ttbufs.c_iflag = ttbuf.c_iflag;
			ttbufs.c_cflag = ttbuf.c_cflag;
			for(i = 0; i < NCC; i++)
				ttbufs.c_cc[i] = ttbuf.c_cc[i];
		}
	}
	for (ps = spds; ps->sp_val >= 0; ps++)
		if (ps->sp_val == spwant)
			speed = ps->sp_name;
	DEBUG(4, "sytfixline - speed= %d\n", speed);
	ASSERT(speed >= 0, "BAD SPEED", "", spwant);
	ttbufs.c_iflag &= 0xffff0000;
	ttbufs.c_oflag &= 0xffff0000;
	ttbufs.c_lflag &= 0xffff0000;
	ttbufs.c_cflag &= 0xffff0000;
	cfsetospeed(&ttbufs, speed);
	ttbufs.c_cflag |= (CS8|CLOCAL);
	ttbufs.c_cc[VMIN] = 6;
	ttbufs.c_cc[VTIME] = 1;
	if (istermios < 0) {
		ttbuf.c_lflag = ttbufs.c_lflag;
		ttbuf.c_oflag = ttbufs.c_oflag;
		ttbuf.c_iflag = ttbufs.c_iflag;
		ttbuf.c_cflag = ttbufs.c_cflag;
		for(i = 0; i < NCC; i++)
			ttbuf.c_cc[i] = ttbufs.c_cc[i];
		ret = (*Ioctl)(tty, TCSETAW, &ttbuf);
	} else
		ret = (*Ioctl)(tty, TCSETSW, &ttbufs);

	ASSERT(ret >= 0, "RETURN FROM sytfixline", "", ret);
	trace1(TR_sytfixline, 1);
	return;
}

GLOBAL void
sytfix2line(tty)
int tty;
{
	struct termio ttbuf;
	int ret;

	trace2(TR_sytfix2line, 0, tty);
	if ((*Ioctl)(tty, TCGETA, &ttbuf) != 0) {
		trace1(TR_sytfix2line, 1);
		return;
	}
	ttbuf.c_cflag &= ~CLOCAL;
	ttbuf.c_cflag |= CREAD|HUPCL;
	ret = (*Ioctl)(tty, TCSETAW, &ttbuf);
	ASSERT(ret >= 0, "RETURN FROM sytfix2line", "", ret);
	trace1(TR_sytfix2line, 1);
	return;
}

#endif /* SYTEK */

GLOBAL int
restline()
{
	trace1(TR_restline, 0);
	if (Saved_line == TRUE) {
		trace1(TR_restline, 1);
		if (Saved_termios < 0)
			return ((*Ioctl)(0, TCSETAW, &Savettyb));
		else
			return ((*Ioctl)(0, TCSETSW, &Savettybs));
	}
	trace1(TR_restline, 1);
	return (0);
}

#else /* !ATTSV */

static struct sgttyb Savettyb;

/*
 *	fixline(tty, spwant, type)	set speed/echo/mode...
 *	int tty, spwant;
 *
 *	if spwant == 0, speed is untouched
 *	type is unused, but needed for compatibility
 *
 *	return codes:  none
 */

/*ARGSUSED*/
GLOBAL void
fixline(tty, spwant, type)
int tty, spwant, type;
{
	struct sgttyb	ttbuf;
	struct sg_spds	*ps;
	int		 speed = -1;

	trace4(TR_fixline, 0, tty, spwant, type);
	DEBUG(6, "fixline(%d, ", tty);
	DEBUG(6, "%d)\n", spwant);

	if ((*Ioctl)(tty, TIOCGETP, &ttbuf) != 0) {
		trace1(TR_fixline, 1);
		return;
	}
	if (spwant > 0) {
		for (ps = spds; ps->sp_val; ps++)
			if (ps->sp_val == spwant) {
				speed = ps->sp_name;
				break;
			}
		ASSERT(speed >= 0, "BAD SPEED", "", spwant);
		ttbuf.sg_ispeed = ttbuf.sg_ospeed = speed;
	} else {
		for (ps = spds; ps->sp_val; ps++)
			if (ps->sp_name == ttbuf.sg_ispeed) {
				spwant = ps->sp_val;
				break;
			}
		ASSERT(spwant >= 0, "BAD SPEED", "", ttbuf.sg_ispeed);
	}
	ttbuf.sg_flags = (ANYP | RAW);
	(void) (*Ioctl)(tty, TIOCSETP, &ttbuf);
	(void) (*Ioctl)(tty, TIOCHPCL, STBNULL);
	(void) (*Ioctl)(tty, TIOCEXCL, STBNULL);
	donap = (spwant > 0 && spwant < 4800);
	trace1(TR_fixline, 1);
	return;
}

GLOBAL void
sethup(dcf)
int	dcf;
{
	trace2(TR_sethup, 0, dcf);
	if (isatty(dcf)) 
		(void) (*Ioctl)(dcf, TIOCHPCL, STBNULL);
	trace1(TR_sethup, 1);
	return;
}

/*
 *	genbrk		send a break
 *
 *	return codes;  none
 */

GLOBAL void
ttygenbrk(fn)
{
	trace1(TR_ttygenbrk, 0);
	if (isatty(fn)) {
		(void) (*Ioctl)(fn, TIOCSBRK, 0);
#ifndef V8
		nap(HZ/10);				/* 0.1 second break */
		(void) (*Ioctl)(fn, TIOCCBRK, 0);
#endif
	}
	trace1(TR_ttygenbrk, 1);
	return;
}

#ifndef DIAL
/*
 * V7 and RT aren't smart enough for this -- linebaudrate is the best
 * they can do.
 */
/*ARGSUSED*/
GLOBAL void
setline(dummy) 
{ 
	trace1(TR_setline, 0);
	trace1(TR_setline, 1);
}
#endif

GLOBAL int
savline()
{
	trace1(TR_savline, 0);
	if ((*Ioctl)(0, TIOCGETP, &Savettyb) != 0) {
		Saved_line = FALSE;
	else {
		Saved_line = TRUE;
		Savettyb.sg_flags |= ECHO;
		Savettyb.sg_flags &= ~RAW;
	}
	trace1(TR_savline, 1);
	return (0);
}

GLOBAL int
restline()
{
	trace1(TR_restline, 0);
	if (Saved_line == TRUE) {
		trace1(TR_restline, 1);
		return ((*Ioctl)(0, TIOCSETP, &Savettyb));
	}
	trace1(TR_restline, 1);
	return (0);
}
#endif
