vnews bug fixes (virtterm.c)

chuqui at nsc.UUCP chuqui at nsc.UUCP
Sat Jun 2 00:27:12 AEST 1984


This is the other half of vnews, virtterm.c.

---- virtterm.c ----

/*
 *  Virtual terminal handler for the HP-2621 terminal.
 *  Written by Kenneth Almquist, AGS Computers  (HO 4C601, X7105).
 *  Modified by Stephen Hemminger, to use TERMCAP (without curses)
 */

#include <stdio.h>
#include <ctype.h>

#define MAXPLEN 24
#define MAXLLEN 80
#define BOTLINE (ROWS - 1)
#define DIRTY 01

/* terminal escape sequences from termcap */
#define HO _tstr[0]		/* home */
#define CL _tstr[1]		/* clear screen */
#define CD _tstr[2]		/* clear to end of screen */
#define CE _tstr[3]		/* clear to end of line */
#define UP _tstr[4]		/* up one line */
#define DO _tstr[5]		/* down one line */
#define US _tstr[6]		/* underline */
#define UE _tstr[7]		/* underline end */
#define BT _tstr[8]		/* backtab */
#define BC _tstr[9]		/* backspace */
#define AL _tstr[10]		/* insert line */
#define DL _tstr[11]		/* delete line */
#define CM _tstr[12]		/* cursor move */
#define CH _tstr[13]		/* cursor horizontal move */
#define CV _tstr[14]		/* cursor vertical move */
#define CS _tstr[15]		/* scrolling region */
#define SF _tstr[16]		/* scroll forwards */
#define SR _tstr[17]		/* scroll backwards */
#define TI _tstr[18]		/* start cursor mode */
#define TE _tstr[19]		/* end cursor mode */
#define TA _tstr[20]		/* tab char (if not \t) */
#define CR _tstr[21]		/* carriage return (if not \r) */
#define xPC _tstr[22]		/* for reading pad character */
char PC;			/* pad character */

static char sname[] = "hoclcdceupdousuebtbcaldlcmchcvcssfsrtitetacrpc";
char *_tstr[23];
int     HOlen;			/* length of HO string */


/* terminal flags */
#define BS _tflg[0]		/* can backspace */
#define AM _tflg[1]		/* has auto margins */
#define XN _tflg[2]		/* no newline after wrap */
#define RET !_tflg[3]		/* has carriage return */
#define NS _tflg[4]		/* has SF (scroll forward) */
#define PT _tflg[5]		/* has tabs */
#define XT _tflg[6]		/* tabs are destructive */
int	GT = 1;			/* tab stops on terminal are set */

static char bname[] = "bsamxnncnsptxt";
char _tflg[7];


extern char *tgoto(), *tgetstr();
extern char *getenv(), *strcpy();

#define ULINE 0200
#define CURSEEN 1

/* Constants accessable by user */
int     hasscroll;		/* scrolling type, 0 == no scrolling */
int     ROWS;			/* number of lines on screen */
int     COLS;			/* width of screen */

struct line {
	char    len;
	char    flags;
	char    l[MAXLLEN];
};

int     _row, _col;
int     _srow, _scol;
struct line _virt[MAXPLEN], _actual[MAXPLEN];
int     _uline = 0;
int     _junked = 1;
int     _curjunked;
int     _dir = 1;
int	_shifttop, _shiftbot;
int	_shift;
int	_scratched;
int     vputc();



/*
 * Tell refresh to shift lines in region upwards count lines.  Count
 * may be negative.  The virtual image is not shifted; this may change
 * later.  The variable _scratched is set to supress all attempts to
 * shift.
 */

ushift(top, bot, count) {
	if (_scratched)
		return;
	if (_shift != 0 && (_shifttop != top || _shiftbot != bot)) {
		_scratched++;
		return;
	}
	_shifttop = top;
	_shiftbot = bot;
	_shift += count;
}



/*
 * generate a beep on the terminal
 */

beep() {
	vputc('\7');
}

/*
 * Move to one line below the bottom of the screen.
 */

botscreen() {
	_amove(BOTLINE, 0);
	vputc('\n');
	vflush();
}



move(row, col) {
	if (row < 0 || row >= ROWS || col < 0 || col >= COLS)
		return;
	_row = row;
	_col = col;
}



/*
 * Output string at specified location.
 */

mvaddstr(row, col, str)
	char *str;
	{
	move(row, col);
	addstr(str);
}


addstr(s)
char   *s;
{
	register char  *p;
	register struct line   *lp;
	register int    col = _col;

	lp = &_virt[_row];
	if (lp->len < col) {
		p = &lp->l[lp->len];
		while (lp->len < col) {
			*p++ = ' ';
			lp->len++;
		}
	}
	for (p = s; *p != '\0'; p++) {
		if (*p == '\n') {
			lp->len = col;
			lp->flags |= DIRTY;
			col = 0;
			if (++_row >= ROWS)
				_row = 0;
			lp = &_virt[_row];
		}
		else {
			lp->l[col] = *p;
			lp->flags |= DIRTY;
			if (++col >= COLS) {
				lp->len = COLS;
				col = 0;
				if (++_row >= ROWS)
					_row = 0;
				lp = &_virt[_row];
			}
		}
	}
	if (lp->len <= col)
		lp->len = col;
	_col = col;
}



addch (c) {
	register struct line   *lp;
	register char  *p;

	lp = &_virt[_row];
	if (lp->len < _col) {
		p = &lp->l[lp->len];
		while (lp->len < _col) {
			*p++ = ' ';
			lp->len++;
		}
	}
	lp->l[_col] = c;
	if (lp->len == _col)
		lp->len++;
	if (++_col >= COLS) {
		_col = 0;
		if (++_row >= ROWS)
			_row = 0;
	}
	lp->flags |= DIRTY;
}



clrtoeol() {
	register struct line   *lp;

	lp = &_virt[_row];
	if (lp->len > _col) {
		lp->len = _col;
		lp->flags |= DIRTY;
	}
}


/*
 * Clear an entire line.
 */

clrline(row) {
	register struct line   *lp;

	lp = &_virt[row];
	if (lp->len > 0) {
		lp->len = 0;
		lp->flags |= DIRTY;
	}
}



clear() {
	erase();
	_junked++;
}



erase() {
	register    i;

	for (i = 0; i < ROWS; i++) {
		_virt[i].len = 0;
		_virt[i].flags |= DIRTY;
	}
}



refresh() {
	register i;
	int j, len;
	register char *p, *q;

	if (checkin())
		return;
	i = 1;
	if (_junked) {
		_sclear();
		_junked = 0;
	} else if (! _scratched) {
		if (_shift > 0) {
			_ushift(_shifttop, _shiftbot, _shift);
		} else if (_shift < 0) {
			i = _dshift(_shifttop, _shiftbot, -_shift);
		} else {
			i = _dir;
		}
	} 
	_dir = i;
	_shift = 0;
	if (checkin())
		return;
	_fixlines();
	for (i = _dir > 0 ? 0 : BOTLINE; i >= 0 && i < ROWS; i += _dir) {
		if ((_virt[i].flags & DIRTY) == 0)
			continue;
		_ckclrlin(i);		/* decide whether to do a clear line */
					/* probably should consider cd as well */
		len = _virt[i].len;
		if (_actual[i].len < len)
			len = _actual[i].len;
		p = _virt[i].l;
		q = _actual[i].l;
		for (j = 0; j < len; j++) {
			if (*p != *q) {
				_amove(i, j);
				_aputc(*p);
				*q = *p;
			}
			p++, q++;
		}
		len = _virt[i].len;
		if (_actual[i].len > len) {
			_clrtoeol(i, len);
		}
		else {
			for (; j < len; j++) {
				if (*p != ' ') {
					_amove(i, j);
					_aputc(*p);
				}
				*q++ = *p++;
			}
			_actual[i].len = len;
		}
		if (checkin())
			return;
	}
	_dir = 1;
	if (CURSEEN)
		_amove(_row, _col);
	vflush();			/* flush output buffer */
	_scratched = 0;
}


_dshift(top, bot, count) {
	register    i;

	if (count >= bot - top || hasscroll < 4) {  /* must have CS or AL/DL */
		_scratched++;
		return 1;
	}
	for (i = bot - count; _actual[i].len == 0; i--)
		if (i == top)
			return 1;
	for (i = top; i <= bot; i++)
		_virt[i].flags |= DIRTY;
	for (i = bot; i >= top + count; i--)
		_actual[i] = _actual[i - count];
	for (; i >= top; i--)
		_actual[i].len = 0;

	if (hasscroll != 5) {		/* can we define scrolling region, and scroll back */
		tputs(tgoto(CS, bot, top), 1, vputc);/* define scroll region */
		_curjunked = 1;
		_amove(top, 0);
		for (i = count; --i >= 0;)
			tputs(SR, 1, vputc);/* scroll back */
		tputs(tgoto(CS, BOTLINE, 0), 1, vputc);
		_curjunked = 1;
	} else {
		_amove(bot - count + 1, 0);
		if (CD && bot == BOTLINE)
			tputs(CD, 1, vputc);
		else {
			for (i = count; --i >= 0;)
				tputs(DL, ROWS - _srow, vputc);
		}
		_amove(top, 0);
		for (i = count; --i >= 0;)
			tputs(AL, ROWS - _srow, vputc);
	}
	return -1;
}


_ushift(top, bot, count) {
	register    i;

	if (count >= bot - top || hasscroll == 0) {
		_scratched++;
		return;
	}
	for (i = top + count; _actual[i].len == 0; i++)
		if (i == bot)
			return;
	if (hasscroll == 1 || hasscroll == 3) {
		/* we cheat and shift the entire screen */
		/* be sure we are shifting more lines into than out of position */
		if ((bot - top + 1) - count <= ROWS - (bot - top + 1))
			return;
		top = 0, bot = BOTLINE;
	}
	for (i = top; i <= bot; i++)
		_virt[i].flags |= DIRTY;
	for (i = top; i <= bot - count; i++)
		_actual[i] = _actual[i + count];
	for (; i <= bot; i++)
		_actual[i].len = 0;

	if (hasscroll != 5) {
		if (top != 0 || bot != BOTLINE) {
			tputs(tgoto(CS, bot, top), 0, vputc);
			_curjunked = 1;
		}
		_amove(bot, 0);	/* move to bottom */
		for (i = 0; i < count; i++) {
			if (SF)		/* scroll forward */
				tputs(SF, 1, vputc);
			else
				vputc('\n');
		}
		if (top != 0 || bot != BOTLINE) {
			tputs(tgoto(CS, BOTLINE, 0), 0, vputc);
			_curjunked = 1;
		}
	} else {
		_amove(top, 0);
		for (i = count; --i >= 0;)
			tputs(DL, ROWS - _srow, vputc);
		if (bot < BOTLINE) {
			_amove(bot - count + 1, 0);
			for (i = count; --i >= 0;)
				tputs(AL, ROWS - _srow, vputc);
		}
	}
}


_sclear() {
	register struct line   *lp;

	tputs(CL, 0, vputc);
	_srow = _scol = 0;
	for (lp = _actual; lp < &_actual[ROWS]; lp++) {
		lp->len = 0;
	}
	for (lp = _virt; lp < &_virt[ROWS]; lp++) {
		if (lp->len != 0)
			lp->flags |= DIRTY;
	}
}


_clrtoeol(row, col) {
	register struct line *lp = &_actual[row];
	register i;

	if (CE && lp->len > col + 1) {
		_amove(row, col);
		tputs(CE, 1, vputc);
	} else {
		for (i = col ; i < lp->len ; i++) {
			if (lp->l[i] != ' ') {
				_amove(row, i);
				_aputc(' ');
			}
		}
	}
	lp->len = col;
}


_fixlines() {
	register struct line   *lp;
	register char  *p;
	register int    i;

	for (i = 0; i < ROWS; i++) {
		lp = &_virt[i];
		if (lp->flags & DIRTY) {
			lp = &_virt[i];
			for (p = &lp->l[lp->len]; --p >= lp->l && *p == ' ';);
			lp->len = p + 1 - lp->l;
			if (lp->len == _actual[i].len && strncmp(lp->l, _actual[i].l, lp->len) == 0)
				lp->flags &= ~DIRTY;
		}
	}
}


/*
 * Consider clearing the line before overwriting it.
 * We always clear a line if it has underlined characters in it
 * because these can cause problems.  Otherwise decide whether
 * that will decrease the number of characters to change.  This
 * routine could probably be simplified with no great loss.
 */

_ckclrlin(i) {
	int     eval;
	int     len;
	int     first;
	register struct line   *vp, *ap;
	register int    j;

	if (!CE)
		return;
	ap = &_actual[i];
	vp = &_virt[i];
	len = ap->len;
	eval = -strlen(CE);
	if (len > vp->len) {
		len = vp->len;
		eval = 0;
	}
	for (j = 0; j < len && vp->l[j] == ap->l[j]; j++);
	if (j == len)
		return;
	first = j;
	while (j < len) {
		if (vp->l[j] == ' ') {
			if (ap->l[j] != ' ') {
				while (++j < len && vp->l[j] == ' ' && ap->l[j] != ' ') {
					eval++;
				}
				if (j == len)
					eval++;
				continue;
			}
		}
		else {
			if (vp->l[j] == ap->l[j]) {
				while (++j < len && vp->l[j] == ap->l[j]) {
					eval--;
				}
				continue;
			}
		}
		j++;
	}
	if (US) {
		for (j = 0 ; j < ap->len ; j++) {
			if (ap->l[j] & ULINE) {
				eval = 999;
				if (first > j)
					first = j;
				break;
			}
		}
	}
	for (j = first; --j >= 0;)
		if (vp->l[j] != ' ')
			break;
	if (j < 0)
		first = 0;
	if (eval > 0) {
		_amove(i, first);
		tputs(CE, 0, vputc);
		_actual[i].len = first;
	}
}



/*
 * Move routine
 * 	first compute direct cursor address string and cost
 *	then relative motion string and cost,
 *	then home then relative and cost
 *	choose smallest and do it.
 *
 *	The plod stuff is to build the strings (with padding) then decide
 */
static char *plodstr;		/* current location in relmove string */

plodput(c) { *plodstr++ = c; }

_amove(row, col) {
	char direct[20];
	char rel[MAXPLEN + MAXLLEN + 50];    /* longest move is full screen */
	char ho[MAXPLEN + MAXLLEN + 50];
	int cost, newcost;
	register char *movstr;

	if (row == _srow && col == _scol && _curjunked == 0)
		return;
	_setul(0);

	cost = 999;
	if (CM) {
		plodstr = direct;
		tputs(tgoto(CM, col, row), 0, plodput);
		*plodstr = '\0';
		cost = plodstr - direct;
		movstr = direct;
	}
	if (_curjunked == 0) {
		plodstr = rel;
		if (_vmove(_srow, row) >= 0
		 && _hmove(_scol, col, row) >= 0
		 && (newcost = plodstr - rel) < cost) {
			*plodstr = '\0';
			cost = newcost;
			movstr = rel;
		}
	}
	if (cost > HOlen) {	/* is it worth calculating */
		plodstr = ho;
		tputs(HO, 0, plodput);
		if (_vmove(0, row) >= 0
		 && _hmove(0, col, row) >= 0
		 && (newcost = plodstr - ho) < cost) {
			*plodstr = '\0';
			cost = newcost;
			movstr = ho;
		}
	}

	while (--cost >= 0) {
		vputc(*movstr++);
	}
	_srow = row, _scol = col;
	_curjunked = 0;
}


_vmove(orow, nrow) {
	char direct[20];
	char *saveplod = plodstr;

	if (CV) {
		plodstr = direct;
		tputs(tgoto(CV, nrow, nrow), 0, plodput);
		*plodstr = '\0';
		plodstr = saveplod;
	}
	if (orow > nrow) {		/* cursor up */
		if (! UP)
			return -1;
		while (orow > nrow) {
			tputs(UP, 1, plodput);
			orow--;
		}
	}
	while (orow < nrow) {		/* cursor down */
		if (DO)
			tputs(DO, 1, plodput);
		else
			*plodstr++ = '\n';
		orow++;
	}
	if (CV && plodstr - saveplod >= strlen(direct)) {
		register char *p;
		plodstr = saveplod;
		for (p = direct ; *plodstr = *p++ ; plodstr++);
	}
	return 0;
}


_hmove(ocol, ncol, row) {
	char direct[20];
	char ret[MAXLLEN + 50];
	char *saveplod = plodstr;
	char *movstr;
	int cost, newcost;

	cost = 999;
	if (CH) {
		plodstr = direct;
		tputs(tgoto(CH, ncol, ncol), 0, plodput);
		cost = plodstr - direct;
		movstr = direct;
		plodstr = saveplod;
	}
	if (RET && ocol > ncol) {	/* consider doing carriage return */
		plodstr = ret;
		if (CR)
			tputs(CR, 1, plodput);
		else
			*plodstr++ = '\r';
		if (_relhmove(0, ncol, row) >= 0
		 && (newcost = plodstr - ret) < cost) {
			cost = newcost;
			movstr = ret;
		}
		plodstr = saveplod;
	}
	if (_relhmove(ocol, ncol, row) < 0) {
		if (cost == 999)
			return -1;
		goto copy;
	}
	if (plodstr - saveplod > cost) {
copy:		plodstr = saveplod;
		while (--cost >= 0)
			*plodstr++ = *movstr++;
	}
	return 0;
}



_relhmove(ocol, ncol, row) {
	int tab;

	if (ocol < ncol && PT && GT) {	/* tab (nondestructive) */
		while ((tab = (ocol + 8) & ~07) <= ncol) {
			if (TA)
				tputs(TA, 1, plodput);
			else
				*plodstr++ = '\t';
			ocol = tab;
		}
		if (tab < COLS && tab - ncol < ncol - ocol) {
			if (TA)
				tputs(TA, 1, plodput);
			else
				*plodstr++ = '\t';
			ocol = tab;
		}
	} else if (BT && GT && ocol > ncol) {	/* backwards tab */
		while ((tab = (ocol - 1) &~ 07) >= ncol) {
			if (BS && tab == ocol - 1) {
				if (BC)
					tputs(BC, 1, plodput);
				else
					*plodstr++ = '\b';
			} else
				tputs(BT, 1, plodput);
			ocol = tab;
		}
		if (ncol - tab + 1 < ocol - ncol) {
			tputs(BT, 1, plodput);
			ocol = tab;
		}
	}
	if (ocol > ncol) {			/* cursor left */
		if (! BS)
			return -1;
		while (ocol > ncol) {
			if (BC != NULL)
				tputs(BC, 1, plodput);
			else
				*plodstr++ = '\b';
			ocol--;
		}
	}
	if (ocol < ncol) {			/* cursor right */
		register struct line *lp = &_actual[row];
		/*
		 * This code doesn't move over underlined characters properly,
		 * but in practice this doesn't seem to matter.
		 */
		while (ocol < ncol) {
			if (ocol < lp->len)
				*plodstr++ = lp->l[ocol];
			else
				*plodstr++ = ' ';
			ocol++;
		}
	}
	return 0;
}



_aputc(c) {
	if (AM && _scol == COLS - 1 && _srow == ROWS - 1)
		return;
	_setul(c & ULINE);
	vputc(c & ~ULINE);
	if (++_scol >= COLS) {
		if (! AM) {
			_scol--;
		} else  if (XN) {
			_curjunked++;
		} else {
			_scol = 0;
			++_srow;
		}
	}
}


_setul(on) {
	if (on) {
		if (_uline == 0 && US != NULL) {
			tputs(US, 1, vputc);
			_uline = 1;
		}
	}
	else {
		if (_uline != 0 && UE != NULL) {
			tputs(UE, 1, vputc);
			_uline = 0;
		}
	}
}


/*
 * Initialize termcap strings for later use.
 */
initterm() {
	static char tcbuf[1024];	/* termcap buffer */
	register char  *cp;

	if ((cp = getenv("TERM")) == NULL)
		xerror("TERM not set in environment");

	switch (tgetent(tcbuf, cp)) {
		case 0: 
			xerror("Terminal not found in TERMCAP");
		case -1: 
			xerror("Can't open /etc/termcap");
		case 1: 
			break;
	}

	if ((ROWS = tgetnum("li")) == -1
	 || (COLS = tgetnum("co")) == -1)
		xerror("Can't get screen size");
	_zap();

	if (CL == NULL)
		xerror ("No clear screen defined");

	if (HO == NULL && CM == NULL)
		xerror("No home or cursor addressing");
	if (HO)
		HOlen = strlen(HO);
	else
		HOlen = 999;

	PC = xPC ? xPC[0] : 0;

	if (tgetnum("ug") > 0)
		US = UE = NULL;

	if (XT)				/* Destructive tab code not included */
		PT = 0;			/* to keep things simple */

	if (ROWS > MAXPLEN)
		ROWS = MAXPLEN;
	if (COLS > MAXLLEN) {
		COLS = MAXLLEN;
		AM = XN = 1;
	}

	/* Select article scrolling algorithm.  We prefer scrolling region
	   over insert/delete line because it's faster on the HP */
	hasscroll = 0;
	if (!NS) {
		hasscroll = 1;
		if (SR)
			hasscroll = 3;
		if (CS)
			hasscroll++;
	}
	if (AL && DL && hasscroll != 4)
		hasscroll = 5;
}


rawterm() {
	if (TI != NULL)
		tputs(TI, 0, vputc);
}


cookedterm() {
	if (TE != NULL)
		tputs(TE, 0, vputc);
}


/* get strings from termcap */
_zap() {
	static char tstrbuf[1024];
	static char *tp;
	register char  *namp, **sp, *bp;

	tp = tstrbuf;
	sp = _tstr;
	for (namp = sname; *namp; namp += 2) {
		*sp++ = tgetstr(namp, &tp);
	}
	bp = _tflg;
	for (namp = bname; *namp; namp += 2) {
		*bp++ = tgetflag(namp, &tp);
	}
}

-- 
>From the closet of anxieties of:			Chuq Von Rospach
{amd70,fortune,hplabs,ihnp4}!nsc!chuqui			(408) 733-2600 x242

I'm sure I have my death ray in here somewhere...



More information about the Comp.sources.unix mailing list