#include <X/mit-copyright.h>

/* Copyright (c) 1985 Massachusetts Institute of Technology		*/
/* Copyright (c) 1985	Digital Equipment Corporation			*/
 
/* charproc.c */
 
#include <X/Xlib.h>
#include <sys/file.h>
#include "ptyx.h"
#include <stdio.h>
#include <sgtty.h>
#include "chartable.h"
#include "esctable.h"
 
#ifndef lint
static char *rcsid_charproc_c = "$Header: charproc.c,v 10.12 86/06/06 16:59:01 wesommer Exp $";
#endif lint

/* CAUTION: getb "knows" that the variable "c" is receiving the result	*/

#define	getb(buf) (--buf_cnt >= 0 ? *buf_ptr++:				\
	((c=fill(buf)), buf_cnt=buf->cnt, buf_ptr=buf->ptr, c))

#define	peekb(buf)  (buf_cnt >  0 ? *buf_ptr  : -1)

#define ANSIflush()   { arg = flushbuf(screen, term->flags, text_ptr);	\
			buffermode = 0;					\
			text_ptr = text_buf;				\
			text_cnt = TEXT_BUF_SIZE; }

#define TEKflush()	{	arg = TekFlushBuf(term, text_ptr);	\
				buffermode = 0;				\
				text_ptr = text_buf;			\
				text_cnt = TEXT_BUF_SIZE;		\
			}

#define	TEXT_BUF_SIZE	256

extern TekLink *tb_end_link;
extern int tb_end;

#ifdef vms             /* forward declaration */
bitset();
#endif vms

unsigned char	text_buf[TEXT_BUF_SIZE];
 
unsigned	nri	= 0;
unsigned	nlf	= 0;
unsigned	ctotal	= 0;
unsigned	ntotal	= 0;
 
/*
 * in normal mode, buffer up as large a string of printing characters
 * as possible before flushing the buffer.  buffer up LF's and RI's
 * so these can be processed as a block.
 */
ANSInormal(term)
Terminal	*term;
{
	register int		c;
	register unsigned char	*text_ptr;
	register int		text_cnt;
	register unsigned char	*buf_ptr;
	register int		buf_cnt;
	register unsigned	buffermode;
	register Screen *screen = &term->screen;
	register Buffer *trmbuf = &term->buf;
	int			arg;
 
	buffermode = 0;
	arg = 0;
	c = 0;
	text_ptr = text_buf;
	text_cnt = TEXT_BUF_SIZE;
	buf_cnt = trmbuf->cnt;
	buf_ptr = trmbuf->ptr;
 
	if (screen->TekGMode || screen->TekAMode)
		return (TekMode(term));

	for (;;) {
		/*
		 * if special condition has occurred, c will already
		 * be set to EOF.  otherwise, pick up a new char.
		 */
		if (c >= 0)
			c = getb(trmbuf);

		if (ctable[c] == CPRINTING) {
			buffermode = BUFFER_MODE;
			if (--text_cnt >= 0)
				*text_ptr++ = c;
			else {
				ANSIflush();
				--text_cnt, *text_ptr++ = c;
				if (arg)
					c = EOF;
			}
			continue;

		}

		switch (ctable[c]) {
		case CIGNORE:
			break;


		case CLINEFEED:
			if (buffermode || nri) {
				ANSIflush();
				if (arg)
					c = EOF;
			}
			++nlf;
			break;

		case CRETURN:
			if (buffermode) {
				ANSIflush();
				if (arg)
					c = EOF;
			}
			screen->cur_col = 0;
			screen->do_wrap = 0;
			break;

		case CRI:
RI_kluge:		if (buffermode || nlf) {
				ANSIflush();
				if (arg)
					c = EOF;
			}
			++nri;
			break;

		case FLUSH:
			ANSIflush();
			trmbuf->cnt = buf_cnt;
			trmbuf->ptr = buf_ptr;
			return (arg);

		case CESC:
			/* try to intercept RI's so we can buffer them	*/
			if (peekb(trmbuf) == 'M') {
				c = getb(trmbuf);
				goto RI_kluge;
			}
			/* no RI detected; fall into default case	*/

		default:
			ANSIflush();
			docontrol(term, c);
	
			/*
			 * if mode changes or event occurs, force a
			 * cleanup and return by setting char to EOF
			 */
			if (arg 
			|| screen->mode!=ANSInormal 
			|| screen->TekGMode 
			|| screen->TekAMode)
				c = EOF;
		}
	}
}
 
/*
 * flush the buffer of printing characters.  first, process any LF's or
 * RI's that have been buffered.
 */
flushbuf(screen, flags, text_ptr)
register Screen	*screen;
unsigned	flags;
register unsigned char	*text_ptr;
{
	register unsigned char	*s;
	register unsigned char	*p;
	int			arg = 0;
 
	if (nri != 0) {
		ioctl(screen->display->fd, FIONREAD, &arg);
		RevIndex(screen, nri);
		nri = 0;
	}
	if (nlf != 0) {
		ioctl(screen->display->fd, FIONREAD, &arg);
		Index(screen, nlf);
		if (flags & LINEFEED) {		/* do a carriage return	*/
			screen->cur_col = 0;
			screen->do_wrap = 0;
		}
		nlf = 0;
	}

	s = text_buf;
	/*
	 * if a single shift is in effect, process 
	 * the one character it affects.
	 */
	if (screen->curss!=0 && s<text_ptr) {
		dotext(screen, flags, screen->gsets[screen->curss], s, s+1);
		++s;
		screen->curss = 0;
	}

	/*
	 * characters with and without the high bit set must be processed
	 * separately (they select different character sets).  collect them
	 * into groups before processing for as much efficiency as possible.
	 */
	while (s < text_ptr) {
		p = s;
		if (*s < 0x80) {
			/* character doesn't have high bit set	*/
			while (*s<0x80 && s<text_ptr)
				++s;
			dotext(screen, flags,
				screen->gsets[screen->curgl], p, s);
		}
		else {	/* character has high bit set		*/
			while (*s>=0x80 && s<text_ptr) {
				*s &= 0x7f;
				++s;
			}
			dotext(screen, flags,
				screen->gsets[screen->curgr], p, s);
		}
	}

	return (arg || screen->display->qlen!=0);
}

/*
 * process a string of characters according to the character set indicated
 * by charset.  worry about end of line conditions (wraparound if selected).
 */
dotext(screen, flags, charset, buf, ptr)
register Screen	*screen;
unsigned	flags;
char		charset;
unsigned char	*buf;
unsigned char	*ptr;
{
	register unsigned char	*s;
	register int	len;
	register int	n;
	register int	next_col;

	switch (charset) {
	case 'A':	/* United Kingdom set				*/
		for (s=buf; s<ptr; ++s)
			if (*s == '#')
				*s = '\036';	/* UK pound sign	*/
		break;

	case 'B':	/* ASCII set					*/
		break;

	case '0':	/* special graphics (line drawing)		*/
		for (s=buf; s<ptr; ++s)
			if (*s>=0x5f && *s<=0x7e)
				*s = *s - 0x5f;
		break;

	default:	/* any character sets we don't recognize	*/
		return;
	}

	len = ptr - buf; 
	ptr = buf;
	while (len > 0) {
		n = screen->max_col-screen->cur_col+1;
		if (n <= 1) {
			if (screen->do_wrap && (flags&WRAPAROUND)) {
				Index(screen, 1);
				screen->cur_col = 0;
				screen->do_wrap = 0;
				n = screen->max_col+1;
			}
			else
				n = 1;
		}
		if (len < n)
			n = len;
		next_col = screen->cur_col + n;
		WriteText(screen, ptr, n, flags);
		/*
		 * the call to WriteText updates screen->cur_col.
		 * If screen->cur_col != next_col, we must have
		 * hit the right margin, so set the do_wrap flag.
		 */
		screen->do_wrap = (screen->cur_col < next_col);
		len -= n;
		ptr += n;
	}
}
 
/*
 * write a string str of length len onto the screen at
 * the current cursor position.  update cursor position.
 */
WriteText(screen, str, len, flags)
register Screen	*screen;
register unsigned char	*str;
register int	len;
unsigned	flags;
{
	Font	fnt;
 
 	fnt = 
#ifdef ICONWINDOW
                Icon (screen) ? screen->fnt_icon :
#endif ICONWINDOW
	        (flags & BOLD ? screen->fnt_bold : screen->fnt_norm);
	if (flags & INSERT)
		InsertChar(screen, len);
#ifdef JUMPSCROLL
	if (!(AddToRefresh(screen))) {
		if(screen->scroll_amt)
			FlushScroll(screen);
#endif JUMPSCROLL
	if (flags & INVERSE)
		XText(screen->window, CursorX(screen), CursorY(screen),
			str, len, fnt, screen->background, screen->foreground);
	else
	 	XText(screen->window, CursorX(screen), CursorY(screen),
			str, len, fnt, screen->foreground, screen->background);
	/*
	 * the following statements compile data to compute the average 
	 * number of characters written on each call to XText.  The data
	 * may be examined via the use of a "hidden" escape sequence.
	 */
	ctotal += len;
	++ntotal;
#ifdef JUMPSCROLL
	}
#endif JUMPSCROLL
	ScreenWrite(screen, str, flags, len);
	CursorForward(screen, len);
}
 
/*
 * in Tek mode (graphics or alpha), buffer up as large a string of printing
 * characters as possible before flushing buffer.  in graphics mode the
 * characters are plotted, in alpha mode they are printed.
 */
TekMode(term)
Terminal	*term;
{
	register int		c;
	register unsigned char	*text_ptr;
	register int		text_cnt;
	register unsigned char	*buf_ptr;
	register int		buf_cnt;
	register unsigned	buffermode;
	register Screen *screen = &term->screen;
	register Buffer *trmbuf = &term->buf;
	int			arg;
 
	c = 0;
	buffermode = 0;
	text_ptr = text_buf;
	text_cnt = TEXT_BUF_SIZE;
	buf_cnt = trmbuf->cnt;
	buf_ptr = trmbuf->ptr;

	for (;;) {
		/*
		 * if special condition has occurred, c will already
		 * be set to EOF.  otherwise, pick up a new char.
		 */
		if (c >= 0)
			c = getb(trmbuf);

		if (c > 0)
			c &= 0x7f;

		if (ctable[c]==CPRINTING || (c==DEL && screen->TekGMode)) {
			buffermode = BUFFER_MODE;
			if (--text_cnt >= 0)
				*text_ptr++ = c;
			else {
				TEKflush();
				--text_cnt, *text_ptr++ = c;
				if (arg)
					c = EOF;
			}
			continue;

		}

		switch (ctable[c]) {
		case CIGNORE:
			break;

		case CTEKINIT:
			if (buffermode)
				TEKflush();
			TekInit(term, c);
			if (arg)
				c = EOF;
			break;

		case FLUSH:
			TEKflush();
			trmbuf->cnt = buf_cnt;
			trmbuf->ptr = buf_ptr;
			return (arg);

		default:	/* BEL, BS, HT, LF, VT, FF, CR, ESC, US	*/
				/* and also SI, SO, CAN, SUB		*/
			TEKflush();
			if (screen->TekGMode)
				TekAlph(term, c);
			docontrol(term, c);
	
			/*
			 * if mode changes or event occurs, force a
			 * cleanup and return by setting char to EOF
			 */
			if (arg || screen->mode!=ANSInormal 
			|| (screen->TekGMode==0 && screen->TekAMode==0))
				c = EOF;
		}
	}
}
 
/*
 * flush the buffer of printing characters.
 * use TekString, TekPoint, or TekPlot according to current mode.
 */
TekFlushBuf(term, text_ptr)
Terminal	*term;
register unsigned char	*text_ptr;
{
	register Screen *screen = &term->screen;
	register unsigned char	*s;
 
	s = text_buf;
	if (text_ptr != text_buf) {
		if (screen->TekGMode) {
			if (screen->TekIMode)
				while (s < text_ptr) {
					TekBufPut(*s);
					TekPoint(term, *s++);
				}
			else
				while (s < text_ptr) {
					TekBufPut(*s);
					TekPlot(term, *s++);
				}
		}
		else
			TekString(term, text_buf, text_ptr-text_buf);
	}

	return (screen->display->qlen != 0);
}

/*
 * a string (DCS, OSC, PM, APC) has been encountered in ANSI mode.
 * all printing characters are ignored until a string terminator
 * is seen (other possible exits are CAN, SUB, ESC, and C1 controls).
 */
ANSIstring(term)
Terminal	*term;
{
	register int	c;
	register Screen *screen = &term->screen;
	register Buffer *trmbuf = &term->buf;
	register unsigned char	*buf_ptr;
	register int		buf_cnt;
 
	buf_cnt = trmbuf->cnt;
	buf_ptr = trmbuf->ptr;
	while (screen->mode==ANSIstring && screen->display->qlen==0) {
		if ((c=getb(trmbuf)) == -1)
			break;
		if ((c=parseseq(c, &screen->ansi, screen->TekEmu)) != -1) {
			switch (ctable[c]) {
			case CIGNORE:
			case CPRINTING:
				break;
			case CESC:
			case CCSI:
				screen->mode = ANSInormal;
				procseq(term);
				break;
			case CDCS:
				screen->mode = ANSIstring;
				procseq(term);
				break;
			case CCANCEL:
				screen->mode = ANSInormal;
				break;
			default:
				break;
			}
		}
	}
	trmbuf->cnt = buf_cnt;
	trmbuf->ptr = buf_ptr;
	return (screen->display->qlen != 0);
}
 
/*
 * call parseseq with each new character until the sequence has been
 * parsed.  once parsed, call procseq to process the sequence.  DEC
 * standard says C0 controls in the middle of a sequence are acted
 * on just as if they weren't in the middle of a sequence (ANSI says
 * this is an error condition, but as always doesn't specify how to
 * handle the error).
 */
ANSIparse(term)
Terminal	*term;
{
	register int	c, ch;
	register Screen *screen = &term->screen;
	register Buffer *trmbuf = &term->buf;
	register unsigned char	*buf_ptr;
	register int		buf_cnt;
 
	c = 0;
	buf_cnt = trmbuf->cnt;
	buf_ptr = trmbuf->ptr;

	for (;;) {
		/*
		 * if special condition has occurred, c will already
		 * be set to EOF.  otherwise, pick up a new char.
		 */
		if (c >= 0)
			c = getb(trmbuf);

		if (c == EOF) {
			trmbuf->cnt = buf_cnt;
			trmbuf->ptr = buf_ptr;
			return (screen->display->qlen != 0);
		}

		if ((ch=parseseq(c, &screen->ansi, screen->TekEmu)) != -1) {
			switch (ctable[ch]) {
			case CIGNORE:
				break;

			case CESC:
			case CCSI:
				screen->mode = ANSInormal;
				procseq(term);
				break;

			case CDCS:
				screen->mode = ANSIstring;
				procseq(term);
				break;

			default:
				docontrol(term, ch);
				break;
			}
	
			/*
			 * if mode changes or event occurs, force a
			 * cleanup and return by setting char to EOF
			 */
			if (screen->mode!=ANSIparse || screen->ansi.a_type==0) {
				c = EOF;
				if (screen->mode==ANSIparse)
					screen->mode = ANSInormal;
				screen->ansi.a_type = 0;
				screen->ansi.a_pintro = 0;
				screen->ansi.a_final = 0;
			}
			if (screen->display->qlen != 0);
				c = EOF;

		}
	}
}
 
/*
 * some sort of ANSI sequence has been parsed.  look through the 
 * esctable to look for a match.  if found, apply default parameters
 * and dispatch.
 */
procseq(term)
Terminal	*term;
{
	register ANSI	*ap = &term->screen.ansi;
	register int	i;
	register long	*p;
	register long	a_funct;
#define	SEQKEY		p[0]	/* type, private, & final in a long	*/
#define INTERS		p[1]	/* intermediate(s) in a long		*/
#define	SEQTYPE		p[2]	/* sequence type (dispatch value)	*/
#define	NDEFLT		p[3]	/* number of default parameters		*/
#define DEFAULT(n)	p[4+n]	/* the n'th default parameter (0 origin)*/
#define FIXEDLEN	4	/* fixed (min) length of table entries	*/
 
	/*
	 * just return if any error encountered
	 */
	if (ap->a_nastyf != 0)
		return;

	a_funct = (ap->a_final << 16) | (ap->a_pintro << 8) | ap->a_type;

	/*
	 * step through table, looking for an
	 * entry which matches the current sequence.
	 */
	for (p=esctable; *p!=0; p+=NDEFLT+FIXEDLEN) {
		if (SEQKEY==a_funct
		&& (INTERS==ap->a_inters || INTERS==WILD)) {
			/*
			 * we have a match.
			 * get default values from table.
			 */
			for (i=0; i<NDEFLT; ++i) {
				if (ap->a_dflt[i] || i>=ap->a_nparam)
					ap->a_param[i] = DEFAULT(i);
			}
			if (ap->a_nparam < NDEFLT)
				ap->a_nparam = NDEFLT;
			/*
			 * ready to process the sequence.
			 */
			doescape(term, SEQTYPE, ap);
			break;
		}
	}
}
 
/*
 * this is a big switch of all the ANSI (and other) sequences
 * we know how to handle.  the cases of the switch must
 * have a corresponding entry in the esctable.
 */
doescape(term, kind, ap)
Terminal	*term;
long		kind;
ANSI		*ap;
{
	register Screen *screen = &term->screen;
	register	row, col;
	register	i, top, bot;
	ANSI		reply;
	int		bitset(), bitclr();
	register unsigned char	*text_ptr;
	register int		text_cnt;

	switch (kind) {
	case DECKPAM:
		term->keyboard.flags |= KYPD_APL;
		break;
 
	case DECKPNM:
		term->keyboard.flags &= ~KYPD_APL;
		break;
 
	case DECTC1:
		screen->rx8bit = 0;
		break;
 
	case DECSC:
		screen->sc.row = screen->cur_row;
		screen->sc.col = screen->cur_col;
		screen->sc.flags = term->flags;
		break;
 
	case DECAC1:
		screen->rx8bit = 1;
		break;
 
	case S7C1T:
		screen->tx8bit = 0;
		break;
 
	case S8C1T:
		screen->tx8bit = 1;
		break;
 
	case DECRC:
		term->flags &= ~(BOLD|INVERSE);
		term->flags |= screen->sc.flags&(BOLD|INVERSE);
		CursorSet(screen, screen->sc.row,
			screen->sc.col, term->flags);
		break;
 
	case LS2:
		screen->curgl = 2;
		break;
 
	case LS3:
		screen->curgl = 3;
		break;
 
	case LS3R:
		screen->curgr = 3;
		break;
 
	case LS2R:
		screen->curgr = 2;
		break;
 
	case LS1R:
		screen->curgr = 1;
		break;
 
	case DESIGNATE:
		switch (ap->a_inters) {
		case '(':
			screen->gsets[0] = ap->a_final;
			break;
 
		case ')':
			screen->gsets[1] = ap->a_final;
			break;
 
		case '*':
			screen->gsets[2] = ap->a_final;
			break;
 
		case '+':
			screen->gsets[3] = ap->a_final;
			break;

		case '#':		/* special "hidden" sequence	*/
			if (ap->a_final == '1')
				fprintf(stderr, "avg call = %d char\n", 
					ctotal/ntotal);
			break;

		default:
			break;
		}
		break;
 
	case DA1:
		if (ap->a_param[0]==0) {
			reply.a_type   = CSI;
			reply.a_pintro = '?';
			reply.a_nparam = 1;
			reply.a_param[0] = 6;		/* VT102	*/
			reply.a_inters = 0;
			reply.a_final  = 'c';
			unparseseq(&reply, screen->tx8bit, screen->respond);
		}
		break;
 
	case TBC:
		if (ap->a_param[0]==0)
			TabClear(term->tabs, screen->cur_col);
		else if (ap->a_param[0] == 3)
			TabZonk(term->tabs);
		break;
 
	case SET:
		modes(term, bitset);
		break;
 
	case DECSET:
		dpmodes(term, bitset);
		break;
 
	case RST:
		modes(term, bitclr);
		break;
 
	case DECRST:
		dpmodes(term, bitclr);
		break;
 
	case SGR:
		for (i=0; i<ap->a_nparam; ++i) {
			switch (ap->a_param[i]) {
			case 0:
				term->flags &= ~(INVERSE|BOLD);
				break;
 
			case 1:
			case 4:		/* Underscore, really	*/
			case 5:		/* Blink, really.	*/
				term->flags |= BOLD;
				break;
 
			case 7:
				term->flags |= INVERSE;
			}
		}
		break;
 
	case CPR:
		if (ap->a_param[0]==5) {
			reply.a_type = CSI;
			reply.a_pintro = 0;
			reply.a_nparam = 1;
			reply.a_param[0] = 0;
			reply.a_inters = 0;
			reply.a_final  = 'n';
			unparseseq(&reply, screen->tx8bit, screen->respond);
			break;
		}
		if (ap->a_param[0]==6) {
			reply.a_type = CSI;
			reply.a_pintro = 0;
			reply.a_nparam = 2;
			reply.a_param[0] = screen->cur_row+1;
			reply.a_param[1] = screen->cur_col+1;
			reply.a_inters = 0;
			reply.a_final  = 'R';
			unparseseq(&reply, screen->tx8bit, screen->respond);
			break;
		}
		break;
 
	case DECSTBM:
		top = ap->a_param[0];
		bot = ap->a_param[1];
		if (top < 1)
			top = 1;
		if (bot > screen->max_row+1 || bot < 1)
			bot = screen->max_row+1;
		if (bot > top) {
#ifdef JUMPSCROLL
			if(screen->scroll_amt)
				FlushScroll(screen);
#endif JUMPSCROLL
			screen->top_marg = top-1;
			screen->bot_marg = bot-1;
			CursorSet(screen, 0, 0, term->flags);
		}
		break;
 
	case ICH:
		InsertChar(screen, ap->a_param[0]);
		break;
 
	case CUU:
		CursorUp(screen, ap->a_param[0]);
		break;
	
	case CUD:
		CursorDown(screen, ap->a_param[0]);
		break;
	
	case CUF:
		CursorForward(screen, ap->a_param[0]);
		break;
	
	case CUB:
		CursorBack(screen, ap->a_param[0]);
		break;
	
	case HVP:
	case CUP:
		row = ap->a_param[0];
		col = ap->a_param[1];
 
		screen->cur_x = screen->cur_y = 0;
		screen->TekAMode = 0;
		CursorSet(screen, row-1, col-1, term->flags);
		break;
 
	case ED:
		switch (ap->a_param[0]) {
		case 0:	ClearBelow(screen);
			break;

		case 1:	ClearAbove(screen);
			break;

		case 2:	ClearScreen(screen);
			if (screen->TekEmu)
				TekErase(term);
			break;
		}
		break;
 
	case EL:
		switch (ap->a_param[0]) {
		case 0:	ClearRight(screen);
			break;

		case 1:	ClearLeft(screen);
			break;

		case 2:	ClearLine(screen);
			break;
		}
		break;
 
	case IL:
		InsertLine(screen, ap->a_param[0]);
		break;
 
	case DL:
		DeleteLine(screen, ap->a_param[0]);
		break;
 
	case DCH:
		DeleteChar(screen, ap->a_param[0]);
		break;
 
	case DECALN:
		text_ptr = text_buf;
		text_cnt = TEXT_BUF_SIZE;
		for (i=0; i<256; ++i) {
			if (--text_cnt >= 0)
				*text_ptr++ = i;
			else {
				flushbuf(screen, term->flags, text_ptr);
				text_ptr = text_buf;
				text_cnt = TEXT_BUF_SIZE-1;
				*text_ptr++ = i;
			}
		}
		flushbuf(screen, term->flags, text_ptr);
		break;

	case TEKESCFF:
	case TEKCSIUS:
		CursorSet(screen, 0, 0, term->flags);
		ClearScreen(screen);
		TekErase(term);
		break;

	case TEKESCSUB:
		TekCursor(term);
		break;

	case TEKESCINQ:
		TekInq(term);
		break;

	default:
		Panic("unexpected dispatch (%d) encountered in doescape", kind);
		break;
	}
}
 
/*
 * this is a big switch of all the control characters we know how to handle.
 * the cases of the switch have a corresponding entry in the ctable.
 */
docontrol(term, c)
Terminal	*term;
{
	register Screen *screen = &term->screen;
 
	switch (ctable[c]) {
	case CIGNORE:
		if (screen->TekAMode)
			TekReset(term);
		break;
 
	case CBELL:
		XFeep(0);
		break;
 
	case CBACKSPACE:
		if (screen->TekAMode)
			TekReset(term);
		CursorBack(screen, 1);
		break;
 
	case CTAB:
		screen->cur_col = TabNext(term->tabs, screen->cur_col);
		if (screen->cur_col > screen->max_col)
			screen->cur_col = screen->max_col;
		break;
 
	case CLINEFEED:		/* actually both VT and LF map here	*/
	case CFORMFEED:
		if (screen->TekAMode)
			TekReset(term);
		Index(screen, 1);
		if (term->flags & LINEFEED)
			CarriageReturn(screen);
		break;
 
	case CRETURN:
		if (screen->TekAMode)
			TekReset(term);
		CarriageReturn(screen);
		break;
 
	case CLS1:
		screen->curgl = 1;
		break;
 
	case CLS0:
		screen->curgl = 0;
		break;
 
	case CCANCEL:
		screen->mode = ANSInormal;
		break;
 
	case CESC:
	case CDCS:
	case CCSI:
                if (screen->TekAMode)
                        TekReset(term);
		screen->ansi.a_type  = c;
		screen->ansi.a_pintro = 0;
		screen->ansi.a_final = 0;
		screen->ansi.a_inters = 0;
		screen->ansi.a_nparam = 0;
		screen->ansi.a_nastyf = 0;
		screen->mode = ANSIparse;
		break;
 
	case CTEKINIT:		/* come here from FS, GS, or RS		*/
		if (screen->TekEmu)
			TekInit(term, c);
		break;
 
	case CTEKALPH:		/* US					*/
		break;		/* just ignore; can only get to alpha	*/
				/* mode from Tek graphics mode		*/

	case CIND:
		Index(screen, 1);
		break;
 
	case CNEL:
		Index(screen, 1);
		CarriageReturn(screen);
		break;
 
	case CRI:
		RevIndex(screen, 1);
		break;
 
	case CSS2:
		screen->curss = 2;
		break;
 
	case CSS3:
		screen->curss = 3;
		break;
 
	case CHTS:
		TabSet(term->tabs, screen->cur_col);
		break;

	default:
		Panic("unexpected char (0x%x) encountered in docontrol", c);
		break;
	}
}

/*
 * process ANSI modes set, reset
 */
modes(term, func)
Terminal	*term;
int		(*func)();
{
	register Screen	*screen	= &term->screen;
	register ANSI	*ansi;
	register int	i;

	ansi = &screen->ansi;
	for (i=0; i<ansi->a_nparam; ++i) {
		switch (ansi->a_param[i]) {
		case 4:			/* IRM				*/
			(*func)(&term->flags, INSERT);
			break;

		case 20:		/* LNM				*/
			(*func)(&term->flags, LINEFEED);
			break;
		}
	}
}

/*
 * process DEC private modes set, reset
 */
dpmodes(term, func)
Terminal	*term;
int		(*func)();
{
	register Screen	*screen	= &term->screen;
	register ANSI	*ap;
	register int	i;

	ap = &screen->ansi;
	for (i=0; i<ap->a_nparam; ++i) {
		switch (ap->a_param[i]) {
		case 1:			/* DECCKM			*/
			(*func)(&term->keyboard.flags, CURSOR_APL);
			break;

#ifdef JUMPSCROLL
		case 4:			/* DECSCLM (slow scroll)	*/
			if (func == bitset) {
				screen->jumpscroll = 0;
				if (screen->scroll_amt)
					FlushScroll(screen);
			} else if (!screen->TekEmu)
				screen->jumpscroll = 1;
			(*func)(&term->flags, SMOOTHSCROLL);
			break;
#endif JUMPSCROLL
		case 5:			/* DECSCNM			*/
			i = term->flags;
			(*func)(&term->flags, REVERSE_VIDEO);
			if ((term->flags ^ i) & REVERSE_VIDEO)
				ReverseVideo(term);
			break;

		case 6:			/* DECOM			*/
			(*func)(&term->flags, ORIGIN);
			CursorSet(screen, 0, 0, term->flags);
			break;

		case 7:			/* DECAWM			*/
			(*func)(&term->flags, WRAPAROUND);
			break;
		case 9:			/* MIT bogus sequence		*/
			(*func)(&screen->send_mouse_pos, 1);
			break;
		case 38:		/* DECTEK			*/
			(*func)(&screen->TekEmu, 1);
			/*
			 * probably need to do some work here to get
			 * into or out of Tek emulation cleanly
			 */
			break;
		}
	}
}

/*
 * set a bit in a word given a pointer to the word and a mask.
 */
bitset(p, mask)
int	*p;
{
	*p |= mask;
}

/*
 * clear a bit in a word given a pointer to the word and a mask.
 */
bitclr(p, mask)
int	*p;
{
	*p &= ~mask;
}

ReverseVideo (term)
	Terminal *term;
{
	register Screen *screen = &term->screen;
	int tmp;

#ifndef ICONWINDOW
	XDefineCursor(screen->window,
		      (term->flags & REVERSE_VIDEO) ? screen->rcurs : screen->curs);
#else ICONWINDOW
	if (screen->iconwindow)
		XDefineCursor(screen->iconwindow,
			      (term->flags & REVERSE_VIDEO) ? screen->rcurs : screen->curs);
	XDefineCursor(screen->fullwindow,
		      (term->flags & REVERSE_VIDEO) ? screen->rcurs : screen->curs);
#endif ICONWINDOW
	tmp = screen->background;
	if (screen->cursorcolor == screen->foreground)
	    screen->cursorcolor = tmp;
	screen->background = screen->foreground;
	screen->foreground = tmp;
	XFreePixmap(screen->bgndtile);
	screen->bgndtile = XMakeTile(screen->background);
	if (screen->borderwidth &&
	    screen->background < 2 &&
	    screen->foreground < 2) {
		if (screen->bgndtile == BlackPixmap)
		    screen->bordertile = WhitePixmap;
		else if (screen->bgndtile == WhitePixmap)
		    screen->bordertile = BlackPixmap;
#ifndef ICONWINDOW		
	    XChangeBorder (screen->window, screen->bordertile);
#else ICONWINDOW
	    XChangeBorder (screen->fullwindow, screen->bordertile);
	    if (screen->iconwindow)
		    XChangeBorder (screen->iconwindow, screen->bordertile);
#endif ICONWINDOW
	}
#ifndef ICONWINDOW
	XChangeBackground (screen->window, screen->bgndtile);
#else ICONWINDOW
	XChangeBackground (screen->fullwindow, screen->bgndtile);
	if (screen->iconwindow)
		    XChangeBackground (screen->iconwindow, screen->bgndtile);
#endif ICONWINDOW
	XClear (screen->window);
	ScrnRefresh (screen, 0, 0, screen->max_row +1, screen->max_col + 1);
	if (screen->TekEmu) TekRefresh (term);
}
