/*
 * Name:	MicroEMACS
 *		Ultrix-32 terminal I/O.
 * Version:	29
 * Last edit:	05-Feb-86
 * By:		rex::conroy
 *		decvax!decwrl!dec-rhea!dec-rex!conroy
 *
 * The functions in this file
 * negotiate with the operating system for
 * keyboard characters, and write characters to
 * the display in a barely buffered fashion.
 */
#include	"def.h"

#include	<sgtty.h>

#define	NOBUF	512			/* Output buffer size.		*/
#define	emask		(*((int *) 0X003724L))

char	obuf[NOBUF];			/* Output buffer.		*/
int	nobuf;
struct	sgttyb	oldtty;			/* V6/V7 stty data.		*/
struct	sgttyb	newtty;
struct	tchars	oldtchars;		/* V7 editing.			*/
struct	tchars	newtchars;
struct	ltchars oldltchars;		/* 4.2 BSD editing.		*/
struct	ltchars	newltchars;
int	nrow;				/* Terminal size, rows.		*/
int	ncol;				/* Terminal size, columns.	*/

static  int olde;

/*
 * This function gets called once, to set up
 * the terminal channel. On Ultrix is's tricky, since
 * we want flow control, but we don't want any characters
 * stolen to send signals. Use CBREAK mode, and set all
 * characters but start and stop to 0xFF.
 */
ttopen()
{
	register char	*cp;
	extern char	*getenv();

        (void) sterm(15);
        olde = emask;
        emask = 0XFFFFFFFF;

	if (ioctl(0, TIOCGETP, &oldtty) < 0)
		abort();
	newtty.sg_ospeed = oldtty.sg_ospeed;
	newtty.sg_ispeed = oldtty.sg_ispeed;
	newtty.sg_erase  = oldtty.sg_erase;
	newtty.sg_kill   = oldtty.sg_kill;
	newtty.sg_flags  = oldtty.sg_flags;
	newtty.sg_flags &= ~(ECHO|CRMOD);	/* Kill echo, CR=>NL.	*/
	newtty.sg_flags |= CBREAK;		/* Half-cooked mode.	*/
	if (ioctl(0, TIOCSETP, &newtty) < 0)
		abort();
	if (ioctl(0, TIOCGETC, &oldtchars) < 0)
		abort();
	newtchars.t_intrc  = 0xFF;		/* Interrupt.		*/
	newtchars.t_quitc  = 0xFF;		/* Quit.		*/
	newtchars.t_startc = 0x11;		/* ^Q, for terminal.	*/
	newtchars.t_stopc  = 0x13;		/* ^S, for terminal.	*/
	newtchars.t_eofc   = 0xFF;
	newtchars.t_brkc   = 0xFF;
	if (ioctl(0, TIOCSETC, &newtchars) < 0)
		abort();
	if (ioctl(0, TIOCGLTC, &oldltchars) < 0)
		abort();
	newltchars.t_suspc  = 0xFF;		/* Suspend #1.		*/
	newltchars.t_dsuspc = 0xFF;		/* Suspend #2.		*/
	newltchars.t_rprntc = 0xFF;
	newltchars.t_flushc = 0xFF;		/* Output flush.	*/
	newltchars.t_werasc = 0xFF;
	newltchars.t_lnextc = 0xFF;		/* Literal next.	*/
	if (ioctl(0, TIOCSLTC, &newltchars) < 0)
		abort();
	if ((cp=getenv("TERMCAP")) == NULL
	|| (nrow=getvalue(cp, "li")) <= 0
	|| (ncol=getvalue(cp, "co")) <= 0) {
		nrow = 24;
		ncol = 80;
	}
	if (nrow > NROW)			/* Don't crash if the	*/
		nrow = NROW;			/* termcap entry is	*/
	if (ncol > NCOL)			/* too big.		*/
		ncol = NCOL;
}

/*
 * This routine scans a string, which is
 * actually the return value of a getenv call for the TERMCAP
 * variable, looking for numeric parameter "name". Return the value
 * if found. Return -1 if not there. Assume that "name" is 2
 * characters long. This limited use of the TERMCAP lets us find
 * out the size of a window on the X display.
 */
getvalue(cp, name)
register char	*cp;
register char	*name;
{
	for (;;) {
		while (*cp!=0 && *cp!=':')
			++cp;
		if (*cp++ == 0)			/* Not found.		*/
			return (-1);
		if (cp[0]==name[0] && cp[1]==name[1] && cp[2]=='#')
			return (atoi(cp+3));	/* Stops on ":".	*/
	}
}

/*
 * This function gets called just
 * before we go back home to the shell. Put all of
 * the terminal parameters back.
 */
ttclose()
{
	ttflush();
	if (ioctl(0, TIOCSLTC, &oldltchars) < 0)
		abort();
	if (ioctl(0, TIOCSETC, &oldtchars) < 0)
		abort();
	if (ioctl(0, TIOCSETP, &oldtty) < 0)
		abort();
        sterm(0);
        emask = olde;
}

/*
 * Write character to the display.
 * Characters are buffered up, to make things
 * a little bit more efficient.
 */
ttputc(c)
{
	if (nobuf >= NOBUF)
		ttflush();
	obuf[nobuf++] = c;
}

/*
 * Flush output.
 */
ttflush()
{
	if (nobuf != 0) {
		write(1, obuf, nobuf);
		nobuf = 0;
	}
}

/*
 * Read character from terminal.
 * All 8 bits are returned, so that you can use
 * a multi-national terminal.
 */
ttgetc()
{
	char	buf[1];

	while (read(0, &buf[0], 1) != 1)
		;
	return (buf[0] & 0xFF);
}
