DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - download
Index: ┃ T t

⟦310ee9c09⟧ TextFile

    Length: 33018 (0x80fa)
    Types: TextFile
    Names: »te_window.c«

Derivation

└─⟦a0efdde77⟧ Bits:30001252 EUUGD11 Tape, 1987 Spring Conference Helsinki
    └─ ⟦this⟧ »EUUGD11/euug-87hel/sec1/teco/te_window.c« 

TextFile

/* TECO for Ultrix   Copyright 1986 Matt Fichtenbaum						*/
/* This program and its components belong to GenRad Inc, Concord MA 01742	*/
/* They may be copied if this copyright notice is included					*/

/* te_window.c   window for teco   10/10/86 */
/* This attempts to be a real window, without unecessary redraw */
/* it is very VT-100 specific, and ought to be rewritten to be general */

#include "te_defs.h"

/* maximum screen height and width (horiz and vert, not height and vidth) */
#define W_MAX_V 70
#define W_MAX_H 150
#define MAX 0x7fffffff			/* maximum positive integer, for "last modified" location */
#define W_MARK 0200				/* "this loc is special" in screen image */


/* image of current window */

struct w_line			/* data associated with one screen line */
	{
	int start, end;				/* dot at beginning, at end */
	short n, cflag, col;		/* number of char positions used, line continuation flag, starting col */
	char ch[W_MAX_H];			/* image of line */
	}
	 w_image[W_MAX_V];


/* define "this line is continued" / "this line is a continuation" flags */
#define WF_BEG 1
#define WF_CONT 2

struct w_line *wlp[W_MAX_V];	/* each word points to the corresponding line's data structure */

struct qp w_p1;					/* pointer for window access to buffer */

short curr_x, curr_y;			/* active character position */
short term_x, term_y;			/* current terminal cursor position */
short curs_x, curs_y;			/* current teco dot screen coordinates */
short last_y;					/* last used line in window */
char curs_c;					/* code for char at cursor */
char *curs_p;					/* pointer to cursor loc in window image */
short curs_crflag;				/* flag that cursor is on a CR */
short redraw_sw;				/* forces absolute redraw */


/* fill characters and terminal speeds: 0th entry used when std out is not a terminal */
char win_speeds[] = { 0, 0, B9600, B4800, B2400, B1800, B1200, B600, B300, B200, B150, B134, B110 };
char win_dlye[] =   { 0, 90, 45, 23, 11, 9, 6, 3, 1, 1, 1, 1, 1 };	/* delay for erase-screen */
char win_dlys[] =   { 0, 60, 30, 15, 7, 6, 4, 2, 1, 1, 0, 0, 0 };	/* delay for scroll ops */
char win_dlyl[] =   { 0, 4, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 };		/* delay for erase line */
char win_dlyc[] =   { 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };		/* delay for other control functions */
short win_speed;
\f

/* routine to perform simple scope operations */
/* (an attempt to concentrate VT-100 specific things in one place) */

vt(func)
	int func;
	{
	short t;
	switch (func)
		{
		case VT_CLEAR:			/* clear screen */
			fputs("\033[H\033[J", stdout);
			for (t = 0; t < win_dlye[win_speed]; t++) putchar('\0');	/* fill chars */
			break;

		case VT_EEOL:			/* erase to end of line */
			fputs("\033[K", stdout);
			for (t = 0; t < win_dlyl[win_speed]; t++) putchar('\0');	/* fill chars */
			break;

		case VT_EBOL:			/* erase from beginning of line */
			fputs("\033[1K", stdout);
			for (t = 0; t < win_dlyl[win_speed]; t++) putchar('\0');	/* fill chars */
			break;

		case VT_SETSPEC1:		/* reverse video */
			fputs("\033[7m", stdout);
			break;

		case VT_SETSPEC2:		/* bright reverse video */
			fputs("\033[1;7m", stdout);
			break;

		case VT_CLRSPEC:		/* normal video */
			fputs("\033[0m", stdout);
			break;

		case VT_BS1:			/* backspace 1 spot */
			fputs("\b \b", stdout);
			break;

		case VT_BS2:			/* backspace 2 spots */
			fputs("\b \b\b \b", stdout);
			break;

		case VT_LINEUP:			/* up one line */
			fputs("\033[1A", stdout);
			break;
		}
	}
\f

/* routine to set window parameters */

/* 0: scope type, 1: width, 2: height, 3: seeall, 4: mark position,		*/
/* 5: hold mode, 6: upper left corner position, 7: scroll region size	*/

/*				   0   1		2		 3  4	  5   6	   7	*/
int win_min[]  = { 4,  20,		4,		 0, 0,	 -1,  1,   0 } ;	/* min values for window parameters */
int win_max[]  = { 4,  W_MAX_H,	W_MAX_V, 1, MAX, 12, -1,  20 } ;	/* max values */
int win_data[] = { 4,  132,		24,		 0, 0,	  0,  0,   0 } ;	/* window parameters	*/

int window_size;								/* # of lines in a window */

do_window(ref_flag)
	int ref_flag;					/* nonzero forces "refresh" operation */
	{
	int i;

	if (colonflag && !ref_flag)
		{
		i = get_value(0);	/* get sub-function */
		if ((i < 0) || (i > 7)) ERROR(E_IWA);
		if (!esp->flag2)	/* it's a "get" */
			{
			esp->val1 = win_data[i];
			esp->flag1 = 1;
			}
		else
			{
			if ((esp->val2 < win_min[i]) || (esp->val2 > win_max[i]))	/* check range */
				ERROR(E_IWA);
			if (i == 7)
				{
				if (esp->val2)
					{
					WN_scroll = esp->val2;
					window_size = WN_height - WN_scroll;	/* define size of window area */
					window(WIN_INIT);			/* turn on window */
					}
				else window(WIN_OFF);			/* turn off window */
				}
			win_data[i] = esp->val2;			/* redundant for ~0,7:w, but no harm */
			esp->flag2 = 0;
			window(WIN_REDRAW);					/* redraw window */
			}
		}

	else								/* no colon, or ^W command */
		{
		if (esp->flag1 || ref_flag)
			{
			if (!ref_flag && (esp->val1 == -1000)) redraw_sw = 0;	/* -1000W: "forget that output was done" */
			else window(WIN_DISP);		/* nW or ^W refreshes window */
			}
		 esp->flag2 = esp->flag1 = 0;		/* no colon, consume args */
		}
	colonflag = 0;
	esp->op = OP_START;
	}
\f

/* routine to update screen size with numbers obtained from environment	*/
/* (called by main program's initialization)							*/

set_term_par(lines, cols)
	int lines, cols;
	{
	if ((lines >= win_min[2]) && (lines <= win_max[2])) window_size = win_data[2] = lines;
	if ((cols >= win_min[1]) && (cols <= win_max[1])) win_data[1] = cols;
	}


/* window routine.  performs function as indicated by argument						*/
/* WIN_OFF:		disables split-screen scrolling										*/
/* WIN_SUSP:	disables split-screen scrolling temporarily							*/
/* WIN_INIT:	sets up display support if split-screen scrolling enabled, else nop	*/
/* WIN_RESUME:	re-enables display support											*/
/* WIN_REDRAW:	causes window to be redrawn on next refresh call					*/
/* WIN_REFR:	if scrolling enabled, redoes window, else if ev or es enabled, does	*/
/*				that, else nop														*/
/* WIN_LINE:	does WIN_REFR unless that wouldn't do anything, in which case		*/
/*				it does effective 1EV output										*/

int last_dot = -1;				/* last dot location */

window(arg)
	int arg;
	{
	int i;

	switch (arg)
		{
		case WIN_OFF:				/* final window off */
		case WIN_SUSP:				/* temp window off */
			if (WN_scroll)			/* if reset/clean up */
				{
				/* full margins, cursor to last line, erase line */
				printf("\033[r\033[%d;0H\033[K", WN_height);
				}
			break;

		case WIN_INIT:				/* initialize window - find output speed */
			if (out_noterm) win_speed = 0;		/* std out is not a terminal */
			else
				{
				for (win_speed = 1; (win_speeds[win_speed] != ttybuf.sg_ospeed) && (win_speed < 13); win_speed++);
				if (win_speed == 13) win_speed = 1;
				}
			w_init();				/* set up screen image buffer */
			if (WN_scroll) vt(VT_CLEAR);		/* if split-screen is enabled, clear screen */
									/* (fall through to "resume") */

		case WIN_RESUME:			/* re-enable window */
			if (WN_scroll)			/* set scroll region, cursor to bottom */
				printf("\033[%d;%dr\033[%d;0H", WN_height - WN_scroll + 1, WN_height, WN_height);
			break;
\f

		case WIN_REDRAW:		/* force redraw of window */
			redraw_sw = 1;
			break;

		case WIN_LINE:			/* display one line unless window enabled or ev */
			if (WN_scroll || ev_val) window(WIN_REFR);		/* if a real window is set, do it */
			else if (w_setptr(dot, &w_p1))		/* set pointer to dot... and if there's a buffer */
				{
				w_lines(0, &w_p1, &w_p1);		/* get to beginning of line */
				window0(1);						/* and type 1 line */
				}
			break;

		case WIN_REFR:			/* if enabled, refresh window; else do ev or es */
			if (WN_scroll) window1();		/* if scrolling enabled, refresh the window */
			else if ((ev_val) || (es_val && search_flag))	/* else if ev or es, do that */
				{
				i = (ev_val) ? ev_val : es_val;
				if (w_setptr(dot, &w_p1))		/* set a pointer at dot... and if there's a buffer */
					window0(i - w_lines(1 - i, &w_p1, &w_p1));	/* go back (i-1) lines and ahead (i) lines */
				}
			break;

		case WIN_DISP:					/* display buffer independent of whether scroll mode is enabled */
			window1();
			break;

		}		/* end of switch */

	fflush(stdout);			/* send output out */
	}					/* end of window() */
\f

/* routine to type n lines with character at "dot" in reverse video			*/
/* used for ev, es, and <BS> or <LF> as immediate commands					*/
/* starting char position is in w_p1; argument is number of lines			*/

window0(num)
	int num;
	{
	int wi;
	char wc;			/* temp char */

	for (wi = w_p1.dot; (num > 0) && (wi < z); wi++)		/* for each character */
		{
		wc = w_p1.p->ch[w_p1.c];		/* get character */

		if ((char_count >= WN_width) && (wc != CR) && !(spec_chars[wc] & A_L))	/* if about to exceed width */
			{
			if (et_val & ET_TRUNC) goto w0_noprint;			/* truncate: don't print this */
			else
				{
				fputs("\033[K\015\012\033(0h\033(B ", stdout);	/* <eeol> "NL space" */
				char_count = 2;
				--num;					/* one fewer line remaining */
				}
			}

		if (wi == dot)					/* if this char is at the pointer */
			{
			vt(VT_SETSPEC2);			/* set reverse video */
			if (wc == TAB)
				{
				type_char(' ');		/* illuminate the first sp of a tab */
				vt(VT_CLRSPEC);		/* clear reverse video */
				if (char_count & tabmask) type_char(TAB);
				}
			else						/* not a tab */
				{
				if ((wc == CR) && (char_count < WN_width))	/* CR at rh margin: don't display cursor */
					{
					type_char(' ');		/* cr: put a space after line */
					vt(VT_EEOL);		/* erase to eol */
					}
				type_char(wc);			/* type the char, or exec CR */
				if (wc == LF)
					{
					fputs("\033(0", stdout);
					type_char('e');
					fputs("\033(B", stdout);
					}
				vt(VT_CLRSPEC);		/* clear reverse video */
				}
			}
\f

		else					/* this is not char at pointer */
			{
			if (wc == CR && curr_x < WN_width) vt(VT_EEOL);		/* erase to EOL */
			type_char(wc);
			}
		if ((wc == FF) || (wc == VT))		/* FF & VT end a line */
			{
			vt(VT_EEOL);			/* erase rest of this line */
			crlf();					/* and leave a blank one */
			if (!(ez_val & EZ_NOVTFF)) --num;		/* if FF and VT count as line sep's, count them */
			}

	  w0_noprint:
		if (++w_p1.c > CELLSIZE-1) w_p1.p = w_p1.p->f, w_p1.c = 0;	/* next char */
		if (wc == LF) --num;	/* if this is a line feed, count lines */
		}

	if (dot == z) fputs("\033[1;7m \033[0m\033[0K", stdout);	/* type one space and erase rest of line */
	else fputs("\033[0K", stdout);			/* else just erase to EOL */
	}
\f

/* routine to maintain the screen window										*/
/* if scroll mode is enabled, the VT100 screen is split and only the upper part */
/* is used by this routine; else the whole screen is used.						*/

window1()
	{
	int i, j, m, lflag;

	if (!redraw_sw && (dot == last_dot) && (buff_mod == MAX)) return;		/* return if nothing has changed */

	block_inter(1);							/* disable ^C interrupts */
	if (WN_scroll) printf("\033[1;%dr", window_size);		/* scroll mode: redefine scroll region */
	printf("\033[H");						/* home */
	term_y = term_x = 0;					/* indicate cursor is at home */

	if ((redraw_sw) || (z <= wlp[0]->start)) window1_abs();		/* forced redraw, or z before start of screen */


/* check whether pointer is before modified buffer location */

	else if (buff_mod >= dot)	/* yes */
		{

		if (dot < wlp[0]->start)			/* if dot is before screen */
			{
			w_setptr(wlp[0]->start, &w_p1);	/* get to beginning of screen */

		/* check whether screen begins with the last part of a continued line */
			for (j = 0; (wlp[j]->cflag & WF_CONT) && (j < window_size/2); j++);
			if (j < window_size/2)			/* if so, does it continue less than halfway down the screen? */
				{

				if (j)						/* is there a partial line? */
					{
					w_lines(0, &w_p1, &w_p1);		/* 0L */
					j -= w_lines(1, &w_p1, NULL);		/* now j is number of display lines before screen */
					}
\f

		/* now look for how many lines back "dot" is: if screen starts with partial line, w_p1 has already been moved */
		/* to beginning of the line and j equals the count of extra lines to scroll */

				for (i = 0; (dot < w_p1.dot) && (i < window_size/2); ) i -= w_lines(-1, &w_p1, &w_p1);
				if ((dot >= w_p1.dot) && (i < window_size))			/* found point within reason */
					{
					w_scroll(j - i);			/* scroll screen down that many lines */
					curr_y = wlp[0]->cflag = wlp[0]->col = curr_x = 0;		/* start from top of screen */
					wlp[0]->start = w_p1.dot;	/* save starting char position */
					window2(0);					/* and rewrite screen */
					}

				else window1_abs();				/* farther back than that - redraw */
				}

			else window1_abs();					/* continuation was too long: give up and redraw */
			}				/* end of "dot is before screen" */

		else if (dot <= wlp[last_y]->end) window1_inc(dot);		/* on screen - redraw incrementally */

		else window1_after();				/* dot is after screen: scroll or redraw */
		}				/* end of "dot is before modified point" */


/* the modified point in the buffer is before dot */

	else
		{
		if (buff_mod < wlp[0]->start) window1_abs();	/* modified point before screen - redraw fully */

		else if (buff_mod <= wlp[last_y]->end)			/* modified point on screen */
			{
			for (m = 0; buff_mod > wlp[m]->end; m++);	/* find line with buff_mod */
			w_setptr(wlp[m]->start, &w_p1);				/* set a pointer to start of line with buff_mod */
			j = (m < window_size/2) ? window_size - 1 - m : window_size/2;	/* maximum # of lines between buff_mod & dot */
			for (i = 0; (dot >= w_p1.dot) && (w_p1.dot < z) && (i <= j); )
				i += (lflag = w_lines(1, &w_p1, &w_p1) ) ? lflag : 1;	/* count lines from buff_mod to first line after dot */
			if (i > j) window1_abs();					/* too far - redraw */
			else
				{
				if (lflag && (dot == z)) i++;			/* if at end, following a LF */
				w_setptr(wlp[m]->start, &w_p1);			/* pointer to start of area to redraw */
				if (i >= window_size - m)				/* if there are not enough blank lines on screen */
					w_scroll(i = i - window_size + m), curr_y = m - i, curs_y -= i;	/* scroll up the difference */
				else curr_y = m;
				curr_x = (wlp[curr_y]->cflag & WF_CONT) ? 2 : wlp[curr_y]->col;		/* line starts at left unless continuation */
				if ((curr_y > curs_y) && (curs_y >= 0)) w_rmcurs();		/* remove old cursor if it won't be written over */
				window2(0);							/* rewrite newly cleared region */
				for (curr_x = 0; ++curr_y < window_size; )		/* clear rest of screen if needed */
					{
					wlp[curr_y]->cflag = 0;
					if (wlp[curr_y]->n) wlp[curr_y]->n = 0, vtm(VT_EEOL);
					}
				}
			}			/* end "modified point on screen */

		else window1_after();		/* modified point after screen: scroll or redraw as appropriate */
		}
\f

/* done redrawing: do cleanup work */

	if (WN_scroll)
		{
		printf("\033[%d;%dr", window_size+1, WN_height);	/* reset margins */
		printf("\033[%d;0H", WN_height);		/* cursor to bottom */
		}
	else printf("\033[H");		/* no split screen: set home */

	fflush(stdout);				/* flush output */
	WN_origin = wlp[0]->start;	/* save first char pos on screen */
	redraw_sw = 0;				/* mark screen as updated */
	buff_mod = MAX;
	last_dot = dot;
	block_inter(0);				/* reenable interrupts */
	}
\f

/* routine to redraw screen absolutely */

window1_abs()
	{
	int i, j;

	curr_y = wlp[0]->col = curr_x = 0;				/* indicate where refresh starts */
	set_pointer(dot, &w_p1);						/* make a text buffer, if none, and refresh the display */
	w_lines(0, &w_p1, &w_p1);						/* do 0L */
	if ((i = w_lines(window_size/2, &w_p1, NULL)) == 0) i = 1;		/* check how many lines after dot */
	if (i > window_size/2) i = window_size/2;		/* limit amount after dot */
	for (j = 0; (j < window_size - i) && (w_p1.dot > 0); )		/* find start of display area */
		j -= w_lines(-1, &w_p1, &w_p1);
	if (j > window_size - i) w_lines(1, &w_p1, &w_p1);			/* if too far back, move up one line */

	wlp[0]->start = w_p1.dot;		/* indicate where first window line starts */
	window2(0);						/* refresh the whole display */

	for (curr_x = 0; ++curr_y < window_size; )		/* blank out lines not written by window2 */
		if (wlp[curr_y]->n || redraw_sw) wlp[curr_y]->n = 0, vtm(VT_EEOL);
	}




/* redraw screen incrementally */

window1_inc(wd)
	int wd;						/* argument is earliest change */
	{
	short temp_y;

/* find the line containing the character at wd */

	for (temp_y = 0; wd > wlp[temp_y]->end; temp_y++);

	if ((curs_y != temp_y) || (buff_mod == MAX) || curs_crflag)		/* if the cursor line won't be rewritten */
		w_rmcurs();					/* remove the old cursor */
	curr_y = temp_y;				/* and go to work on the beginning of the line with dot */
	curr_x = (wlp[curr_y]->cflag & WF_CONT) ? 2 : wlp[curr_y]->col;		/* line starts at left unless continuation */

	w_setptr(wlp[curr_y]->start, &w_p1);		/* make a pointer there */
	window2(buff_mod == MAX);		/* if buffer not modified, redraw only the line with dot */

	if (buff_mod < MAX)			/* if buffer has changed, erase display lines beyond end of buffer */
		for (curr_x = 0; ++curr_y < window_size; )
			if ( ((wlp[curr_y]->start >= z) || (wlp[curr_y]->start <= wlp[curr_y-1]->end)) && (wlp[curr_y]->n || redraw_sw) )
				wlp[curr_y]->n = 0, vtm(VT_EEOL), wlp[curr_y]->cflag = 0;
	}
\f

/* routine to move window downwards: scroll up or redraw as appropriate */

window1_after()
	{
	int i, lflag;

	w_rmcurs();						/* remove old cursor */
	w_setptr(wlp[window_size-1]->start, &w_p1);		/* set pointer to start of last line on screen */

	for (i = 0; (dot >= w_p1.dot) && (w_p1.dot < z) && (i <= window_size/2); )
		i += (lflag = w_lines(1, &w_p1, &w_p1)) ? lflag : 1;	/* fwd one line at a time until > dot or end of buffer */

	if (i <= window_size/2)			/* found within n lines */
		{
		if (lflag && (dot == z)) ++i;				/* if dot is at end of buffer after a LF */
		if (i >= window_size - last_y)				/* if there are not enough blank lines on screen */
			w_scroll(i - window_size + last_y), curr_y = window_size - i;	/* scroll up the difference */
		else curr_y = last_y;

		while (curr_y && (wlp[curr_y]->cflag & WF_CONT)) --curr_y;		/* get to start of cont'd lines */
		w_setptr(wlp[curr_y]->start, &w_p1);		/* pointer to start of area to redraw */
		curr_x = wlp[curr_y]->col;					/* redraw starts at line's first column */
		window2(0);									/* rewrite newly cleared region */
		}

	else window1_abs();						/* move down is too far: redraw fully */
	}



/* routine to remove the existing cursor */

w_rmcurs()
	{
	if (curs_c)			/* if there was a cursor */
		{
		w_move(curs_y, curs_x);		/* go remove the old cursor */
		if (curs_c & W_MARK) fputs("\033(0", stdout);		/* if prev char was a spec char */
		putchar(*curs_p = curs_c);	/* put back the char that was there */
		if (curs_c & W_MARK) fputs("\033(B", stdout);
		++term_x;					/* and keep the terminal cursor loc. happy */
		}
	}
\f

/* routine to do actual display refresh												*/
/* called with w_p1 at starting char, curr_y, curr_x at starting coordinate			*/
/* rewrites to end of screen if arg = 0, or only until line with cursor if arg = 1	*/

window2(arg)
	int arg;
	{
	register int wdot;
	register char wc;			/* temp char */
	register short dflag;		/* nonzero if this is char at dot */
	short cr_found;				/* indicates a cr found on this line */

	cr_found = 0;				/* clear "cr" flag in first line written */
	for (wdot = w_p1.dot; (curr_y < window_size) && (wdot < z); wdot++)		/* for each character */
		{
		wc = w_p1.p->ch[w_p1.c] & 0177;		/* get character */
		if (dflag = (wdot == dot)) if (arg) arg = -1;		/* save "this is char at dot", "on line with dot" */

		if (wc < ' ') switch (wc)				/* dispatch control characters */
			{
			case CR:
				if (dflag)			/* if cursor on this CR */
					{
					if (curr_x < WN_width) w_makecurs(' ', 1), w_type(' ', 1);	/* display a space, unless at end */
					else curs_crflag = curs_c = 0;				/* else set "no cursor displayed" */
					}
				/* trim remainder of line if this is first cr and old line was longer */
				if (!cr_found && ((curr_x < wlp[curr_y]->n) || redraw_sw))
					{
					wlp[curr_y]->n = curr_x;
					if (curr_x < WN_width) vtm(VT_EEOL);
					}
				cr_found = 1;			/* set cr flag */
				wlp[curr_y]->cflag &= ~WF_BEG;		/* this line is not continued */
				while (curr_y && (wlp[curr_y]->cflag & WF_CONT)) --curr_y;		/* if line is a continuation, scan up */
				curr_x = 0;
				break;

			case TAB:
				if (curr_x >= WN_width)
					{
					if (et_val & ET_TRUNC) goto noprint;
					if (w_overflow(wdot)) goto w2_exit;				/* extend line */
					}
				if (dflag) w_makecurs(' ', 0);
				w_type(' ', dflag);						/* type one space */
				if (dflag)
					{
					vt(VT_CLRSPEC);		/* end reverse video */
					dflag = 0;
					}
				while ((curr_x & tabmask) && (curr_x < WN_width)) w_type(' ', 0);		/* finish tab */
				break;
\f

			case LF:
				while ((curr_y < window_size) && (wlp[curr_y]->cflag & WF_BEG)) ++curr_y;	/* last screen row of this line */
				wlp[curr_y]->end = wdot;		/* save char position that ended this line */
				if (dflag)		/* if this LF is at dot */
					{			/* put cursor there, save char that was there */
					w_makecurs( (curr_x < wlp[curr_y]->n) ? wlp[curr_y]->ch[curr_x] : ' ', 0);
					fputs("\033(0", stdout);			/* put in a "LF" char */
					w_type('e', 1);
					fputs("\033(B", stdout);
					}			/* if no cr found and not in last column, erase rest of line */
				if (!cr_found && (curr_x < wlp[curr_y]->n))
					{
					wlp[curr_y]->n = curr_x;
					if (curr_x < WN_width) vtm(VT_EEOL);
					}
				if (dflag) --curr_x;			/* put the cursor back before the artificial LF char, if any */
				if (curr_y >= window_size-1)	/* if at end of screen, exit, but... */
					{
					if (dflag) vt(VT_CLRSPEC);	/* if cursor is here, clear reverse video first */
					goto w2_exit;
					}

				if ((wlp[curr_y]->cflag & WF_CONT) && (wlp[curr_y]->end - wlp[curr_y]->start == 1))	/* if a now-empty cont. line, */
					{															/* flush it */
					if (curr_y > 0) wlp[curr_y-1]->cflag &= ~WF_BEG;			/* remove "cont'd" flag from prev line */
					arg = 0;													/* and force redraw of rest of screen */
					if (curs_y == curr_y) curs_c = 0;							/* if cursor was on this line, it will disappear */
					}
				else ++curr_y;				/* down one line if not absorbing blank contin. line */

				wlp[curr_y]->start = wdot + 1;				/* assume line starts with next char */
				wlp[curr_y]->col = curr_x;					/* save starting column */
				cr_found = wlp[curr_y]->cflag = 0;			/* clear line continuation flags */
				if (curr_x) w_ebol();						/* if not at left margin, erase beginning of line */
				if (arg == -1)								/* finished line with dot... quit if spec'd */
					{
					if (dflag)						/* but first, if at cursor, clear reverse video */
						{
						vt(VT_CLRSPEC);
						dflag = 0;
						}
					return;
					}
				break;

			case ESC:
				if (curr_x >= WN_width)
					{
					if (et_val & ET_TRUNC) goto noprint;
					if (w_overflow(wdot)) goto w2_exit;				/* extend line */
					}
				if (dflag) w_makecurs('$', 0);
				w_type('$', dflag);
				break;
\f

			default:					/* all other control chars print as ^X */
				if (curr_x >= WN_width - 1)
					{
					if (et_val & ET_TRUNC) goto noprint;
					if (w_overflow(wdot)) goto w2_exit;
					}
				if (dflag) w_makecurs('^', 0);
				w_type('^', dflag);				/* ^ */
				if (dflag)
					{
					vt(VT_CLRSPEC);		/* if at cursor, clear reverse video */
					dflag = 0;
					}
				w_type(wc | 0100, 0);
				break;
			}					/* end "switch" */
		else					/* a printing character */
			{
			if (curr_x >= WN_width)
				{
				if (et_val & ET_TRUNC) goto noprint;
				if (w_overflow(wdot)) goto w2_exit;				/* extend line */
				}
			if (dflag) w_makecurs(wc, 0);
			w_type(wc, dflag);
			}

		if (dflag)
			{
			vt(VT_CLRSPEC);				/* if at cursor, clear reverse video */
			}

		if ((wc == FF) || (wc == VT))			/* these chars leave a display line blank */
			{
			if (redraw_sw || (curr_x < wlp[curr_y]->n))
				{
				wlp[curr_y]->n = curr_x;
				if (curr_x < WN_width) vtm(VT_EEOL);	/* erase rest of line */
				}
			wlp[curr_y]->end = wdot;
			if (curr_y >= window_size-1) goto w2_exit;	/* quit if overflow screen */
			wlp[++curr_y]->start = wdot + 1;
			cr_found = wlp[curr_y]->cflag = 0;		/* init new line */
			if (curr_x -= 2) w_ebol();		/* back up over ^X; if not at left margin, erase beginning of line */
			wlp[curr_y]->col = curr_x;				/* save starting column */
			}
\f

	  noprint:
		if (++ w_p1.c > CELLSIZE - 1) w_p1.p = w_p1.p->f, w_p1.c = 0;	/* next char in buffer */
		}		/* end of "for all characters" */

	if (dot == z)
		{
		if (curr_x < WN_width) w_makecurs(' ', 1), w_type(' ', 1), vt(VT_CLRSPEC);	/* display a space, unless at end */
		else curs_crflag = curs_c = 0;				/* else set "no cursor displayed" */
		}

	/* clear rest of line if needed */
	if (!cr_found && (redraw_sw || (curr_x < wlp[curr_y]->n)))
		{
		wlp[curr_y]->n = curr_x;
		if (curr_x < WN_width) vtm(VT_EEOL);
		}
	wlp[curr_y]->end = wdot;		/* save char at end of last line */
  w2_exit:
	last_y = curr_y;				/* record last used line on screen */
	}
\f

/* routine to move cursor to current location and then call vt */

vtm(arg)
	int arg;
	{
	w_move(curr_y, curr_x);
	vt(arg);
	}




/* routine to set reverse video and save cursor location */
/* first argument is char at cursor, 2nd is value for curs_crflag */

w_makecurs(wc, crflag)
	char wc;
	short crflag;
	{
	curs_y = curr_y, curs_x = curr_x, curs_c = wc;	/* save cursor coord and char */
	curs_p = &wlp[curr_y]->ch[curr_x];				/* save location of cursor spot in window image */
	curs_crflag = crflag;		/* save crflag */
	vt(VT_SETSPEC2);			/* set flag and reverse video */
	}




/* routine to handle line overflow */
/* returns nonzero if at end of screen, zero otherwise */
/* arg is current character position */

int w_overflow(wd)
	{
	wlp[curr_y]->end = wd-1;			/* last character was end of this line */
	if (wlp[curr_y]->n > curr_x)
		{
		wlp[curr_y]->n = curr_x;
		if (curr_x < WN_width) vtm(VT_EEOL);		/* if old line was wider, erase */
		}
	if (curr_y >= window_size-1) return(1);
	wlp[curr_y]->cflag |= WF_BEG;				/* mark this line as "continued" */
	wlp[++curr_y]->cflag = WF_CONT;				/* next line is a continuation line */
	wlp[curr_y]->start = wd;					/* char about to be printed is this line's first */
	wlp[curr_y]->col = curr_x = 0;				/* new line starts at left margin */
	fputs("\033(0", stdout);					/* alternate char set */
	w_type('h', W_MARK);						/* "NL" space */
	w_type(' ', W_MARK);
	fputs("\033(B", stdout);
	return(0);
	}
\f

/* routine to type one character:  arguments are char and a */
/* "mark" bit.  If mark is set, the char is always retyped  */

w_type(c, m)
	char c;
	int m;
	{
	register char *p;

	p = &wlp[curr_y]->ch[curr_x];		/* pointer to char image */
	if ((c != *p) || (m) || (redraw_sw) || (curr_x >= wlp[curr_y]->n))
		{
		w_move(curr_y, curr_x);
		putchar(c);
		*p = (m) ? c | W_MARK : c;
		++term_x;
		}
	++curr_x;
	if (wlp[curr_y]->n < curr_x) wlp[curr_y]->n = curr_x;	/* if we've lengthened the line, record that fact */
	}




/* initialize display image */

w_init()
	{
	short i, j;

	for (i = 0; i < window_size; i++)		/* for each row */
		{
		wlp[i] = &w_image[i];				/* set pointer to this line's data */
		w_image[i].n = w_image[i].cflag = 0;	/* no chars used, cr flag clear */
		for (j = 0; j < W_MAX_H; w_image[i].ch[j++] = ' ');		/* clear line */
		}
	}




/* put character followed by appropriate number of nulls for "other control function" */
/* if argument is 0, output filler chars only */

putchar_d(c)
	char c;
	{
	int i;

	if (c) putchar(c);												/* output character */
	for (i = 0; i < win_dlyc[win_speed]; i++) putchar('\0');		/* output filler */
	}
\f

/* put out appropriate number of filler chars for display function that scrolls (LF, etc.) */

scroll_dly()
	{
	int i;

	for (i = 0; i < win_dlys[win_speed]; i++) putchar('\0');		/* output filler */
	}



/* move terminal cursor to stated y, x position */
/* uses incremental moves or absolute cursor position, whichever is shorter */

w_move(y, x)
	short y, x;
	{
	register short i;

	/* if practical, use CR to get to left margin */
	if ((curr_x == 0) && (term_x != 0)) putchar(CR), term_x = 0;
	if ((y == term_y) && (term_x < WN_width))		/* if term x is beyond last char, use abs positioning */
		{
		if (x == term_x) return;
		if (x > term_x)
			{
			if (x - term_x == 1) fputs("\033[C", stdout);
			else printf("\033[%dC", x - term_x);
			}
		else
			{
			if ((i = term_x - x) < 4) for (; i > 0; i--) putchar('\b');	/* use BS */
			else printf("\033[%dD", term_x - x);		/* use incremental jump */
			}
		term_x = x;
		}
	else
		{
		if ((x == term_x) && (term_x < WN_width))
			{
			if (y > term_y)
				{
				if ((i = y - term_y) < 4) for (; i >0; i--) putchar(LF);	/* use LF */
				else printf("\033[%dB", i);		/* use incremental jump */
				}
			else if ((i = term_y - y) == 1) fputs("\033[A", stdout);	/* move 1 */
			else printf("\033[%dA", i);
			term_y = y;
			}
		else printf("\033[%d;%dH", (term_y = y) + 1, (term_x = x) + 1);		/* absolute jump */
		}
	}
\f

/* scroll screen: argument is count: + up, - down */

w_scroll(count)
	int count;
	{
	register int i, ic;
	struct w_line *p[W_MAX_V];		/* temp copy of pointer array */

	if (count > 0)		/* scrolling up */
		{
		w_move(window_size-1, 0);	/* cursor to bottom of window */
		for (i = 0; i < count; i++)
			{
			putchar(LF), wlp[i]->n = 0;		/* scroll terminal, blank out image line */
			}
		}
	else		/* scroll down */
		{
		w_move(0, 0);		/* cursor to top */
		for (i = 0; i > count; i--)
			{
			fputs("\033M", stdout), wlp[window_size-1+i]->n = 0;
			}
		}
	for (i = 0; i < window_size; i++) p[i] = wlp[(window_size + i + count) % window_size];	/* rearrange */
	for (i = 0; i < window_size; i++) wlp[i] = p[i];
	}



/* clear line to left of curr_x */
/* if some chars nonblank, does erase from start of line */

w_ebol()
	{
	short i, j;

	for (j = i = 0; i < curr_x; i++) if (wlp[curr_y]->ch[i] != ' ') wlp[curr_y]->ch[i] = ' ', j++;
	if (j || redraw_sw) w_move(curr_y, curr_x-1), vt(VT_EBOL);
	}



/* routine to set a pointer to a given location (like set_pointer) */
/* returns nonzero if a text buffer exists, otherwise 0 */

int w_setptr(loc, pp)
	register int loc;				/* location */
	register struct qp *pp;			/* address of pointer */
	{
	register int i;

	if (buff.f)
		{
		for (i = loc / CELLSIZE, pp->p = buff.f; i > 0; i--) pp->p = pp->p->f;
		pp->c = loc % CELLSIZE;
		pp->dot = loc;
		}
	return( (int) buff.f);
	}
\f

/* routine to move N lines (back, forward, or 0)				*/
/* w_lines(n, &source, &dest) where n is the line count, source	*/
/* points to a qp at the current pointer, dest, if nonzero,		*/
/* it points to a qp where the result is to go.					*/
/* routine returns actual number of display lines				*/

struct qp w_lines_p;				/* to compute # of display lines in -N lines */

int w_lines(n, ps, pd)
	int n;							/* number of lines */
	register struct qp *ps, *pd;	/* source, destination qp's */
	{
	register struct buffcell *tp;	/* local copy of the qp */
	register int tc, tdot, tn;
	int tcnt, tl;					/* chars/line and display line count */
	char tch;

	tdot = ps->dot;
	tp = ps->p;
	tc = ps->c;

	if (n > 0)			/* argument is positive */
		{
		for (tcnt = tl = tn = 0; (tn < n) && (tdot < z); tdot++)	/* forward over N line separators */
			{
			if (spec_chars[ tch = tp->ch[tc] ] & A_L) ++tl, ++tn;		/* count separators */
			else if (!(et_val & ET_TRUNC))		/* if text lines can overflow screen lines */
				{
				if (!(tch & 0140))				/* if character is a control char */
					{
					if (tch == CR)										/* CR resets count */
						{
						if (tcnt > WN_width) ++tl;
						tcnt = 0;
						}
					else if (tch == TAB) tcnt = (tcnt | tabmask) +1;	/* tab to next tab stop */
					else if (tch == ESC) ++tcnt;						/* ESC takes one space */
					else tcnt += 2;										/* normal control chars take 2 spaces */
					}
				else ++tcnt;				/* not a control char: takes one space */
				if (tcnt > WN_width) ++tl, tcnt = 2;		/* if overflow, one more line */
				}

			if (++tc > CELLSIZE-1) tp = tp->f, tc = 0;		/* next character position */
			}
		if (tl > tn) tn = tl;		/* if counting display lines and there's more of them, return that */
		}
\f

	else				/* argument is zero or negative */
		{
		for (tn = 0; (tn >= n) && (tdot > 0); )		/* back up over (n+1) line feeds */
			{
			--tdot;
			if (--tc < 0) tp = tp->b, tc = CELLSIZE -1;
			if (spec_chars[tp->ch[tc]] & A_L) --tn;
			}
		if (tn < n)			/* if stopped on a line sep, fwd over it */
			{
			++tn;
			++tdot;
			if (++tc > CELLSIZE-1) tp = tp->f, tc = 0;
			}

		if (!(et_val & ET_TRUNC) && (n != 0))			/* if text line can overflow display line */
			{
			w_lines_p.dot = tdot, w_lines_p.p = tp, w_lines_p.c = tc;		/* then count the number of display */
			tn = -w_lines(-n, &w_lines_p, 0);				/* lines in the N text lines we just backed up over */
			}
		}

	if (pd) pd->dot = tdot, pd->p = tp, pd->c = tc;		/* if an "after" pointer given, update it */
	return(tn);
	}