GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/mg/display.c Lines: 0 469 0.0 %
Date: 2017-11-07 Branches: 0 316 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: display.c,v 1.48 2017/07/06 19:27:37 schwarze Exp $	*/
2
3
/* This file is in the public domain. */
4
5
/*
6
 * The functions in this file handle redisplay. The
7
 * redisplay system knows almost nothing about the editing
8
 * process; the editing functions do, however, set some
9
 * hints to eliminate a lot of the grinding. There is more
10
 * that can be done; the "vtputc" interface is a real
11
 * pig.
12
 */
13
14
#include <sys/queue.h>
15
#include <ctype.h>
16
#include <signal.h>
17
#include <stdio.h>
18
#include <stdlib.h>
19
#include <string.h>
20
#include <term.h>
21
22
#include "def.h"
23
#include "kbd.h"
24
25
/*
26
 * A video structure always holds
27
 * an array of characters whose length is equal to
28
 * the longest line possible. v_text is allocated
29
 * dynamically to fit the screen width.
30
 */
31
struct video {
32
	short	v_hash;		/* Hash code, for compares.	 */
33
	short	v_flag;		/* Flag word.			 */
34
	short	v_color;	/* Color of the line.		 */
35
	int	v_cost;		/* Cost of display.		 */
36
	char	*v_text;	/* The actual characters.	 */
37
};
38
39
#define VFCHG	0x0001			/* Changed.			 */
40
#define VFHBAD	0x0002			/* Hash and cost are bad.	 */
41
#define VFEXT	0x0004			/* extended line (beond ncol)	 */
42
43
/*
44
 * SCORE structures hold the optimal
45
 * trace trajectory, and the cost of redisplay, when
46
 * the dynamic programming redisplay code is used.
47
 */
48
struct score {
49
	int	s_itrace;	/* "i" index for track back.	 */
50
	int	s_jtrace;	/* "j" index for trace back.	 */
51
	int	s_cost;		/* Display cost.		 */
52
};
53
54
void	vtmove(int, int);
55
void	vtputc(int);
56
void	vtpute(int);
57
int	vtputs(const char *);
58
void	vteeol(void);
59
void	updext(int, int);
60
void	modeline(struct mgwin *, int);
61
void	setscores(int, int);
62
void	traceback(int, int, int, int);
63
void	ucopy(struct video *, struct video *);
64
void	uline(int, struct video *, struct video *);
65
void	hash(struct video *);
66
67
68
int	sgarbf = TRUE;		/* TRUE if screen is garbage.	 */
69
int	vtrow = HUGE;		/* Virtual cursor row.		 */
70
int	vtcol = HUGE;		/* Virtual cursor column.	 */
71
int	tthue = CNONE;		/* Current color.		 */
72
int	ttrow = HUGE;		/* Physical cursor row.		 */
73
int	ttcol = HUGE;		/* Physical cursor column.	 */
74
int	tttop = HUGE;		/* Top of scroll region.	 */
75
int	ttbot = HUGE;		/* Bottom of scroll region.	 */
76
int	lbound = 0;		/* leftmost bound of the current */
77
				/* line being displayed		 */
78
79
struct video	**vscreen;		/* Edge vector, virtual.	 */
80
struct video	**pscreen;		/* Edge vector, physical.	 */
81
struct video	 *video;		/* Actual screen data.		 */
82
struct video	  blanks;		/* Blank line image.		 */
83
84
/*
85
 * This matrix is written as an array because
86
 * we do funny things in the "setscores" routine, which
87
 * is very compute intensive, to make the subscripts go away.
88
 * It would be "SCORE	score[NROW][NROW]" in old speak.
89
 * Look at "setscores" to understand what is up.
90
 */
91
struct score *score;			/* [NROW * NROW] */
92
93
static int	 linenos = TRUE;
94
static int	 colnos = FALSE;
95
96
/* Is macro recording enabled? */
97
extern int macrodef;
98
/* Is working directory global? */
99
extern int globalwd;
100
101
/*
102
 * Since we don't have variables (we probably should) these are command
103
 * processors for changing the values of mode flags.
104
 */
105
/* ARGSUSED */
106
int
107
linenotoggle(int f, int n)
108
{
109
	if (f & FFARG)
110
		linenos = n > 0;
111
	else
112
		linenos = !linenos;
113
114
	sgarbf = TRUE;
115
116
	return (TRUE);
117
}
118
119
/* ARGSUSED */
120
int
121
colnotoggle(int f, int n)
122
{
123
	if (f & FFARG)
124
		colnos = n > 0;
125
	else
126
		colnos = !colnos;
127
128
	sgarbf = TRUE;
129
130
	return (TRUE);
131
}
132
133
/*
134
 * Reinit the display data structures, this is called when the terminal
135
 * size changes.
136
 */
137
int
138
vtresize(int force, int newrow, int newcol)
139
{
140
	int	 i;
141
	int	 rowchanged, colchanged;
142
	static	 int first_run = 1;
143
	struct video	*vp;
144
145
	if (newrow < 1 || newcol < 1)
146
		return (FALSE);
147
148
	rowchanged = (newrow != nrow);
149
	colchanged = (newcol != ncol);
150
151
#define TRYREALLOC(a, n) do {					\
152
		void *tmp;					\
153
		if ((tmp = realloc((a), (n))) == NULL) {	\
154
			panic("out of memory in display code");	\
155
		}						\
156
		(a) = tmp;					\
157
	} while (0)
158
159
#define TRYREALLOCARRAY(a, n, m) do {				\
160
		void *tmp;					\
161
		if ((tmp = reallocarray((a), (n), (m))) == NULL) {\
162
			panic("out of memory in display code");	\
163
		}						\
164
		(a) = tmp;					\
165
	} while (0)
166
167
	/* No update needed */
168
	if (!first_run && !force && !rowchanged && !colchanged)
169
		return (TRUE);
170
171
	if (first_run)
172
		memset(&blanks, 0, sizeof(blanks));
173
174
	if (rowchanged || first_run) {
175
		int vidstart;
176
177
		/*
178
		 * This is not pretty.
179
		 */
180
		if (nrow == 0)
181
			vidstart = 0;
182
		else
183
			vidstart = 2 * (nrow - 1);
184
185
		/*
186
		 * We're shrinking, free some internal data.
187
		 */
188
		if (newrow < nrow) {
189
			for (i = 2 * (newrow - 1); i < 2 * (nrow - 1); i++) {
190
				free(video[i].v_text);
191
				video[i].v_text = NULL;
192
			}
193
		}
194
195
		TRYREALLOCARRAY(score, newrow, newrow * sizeof(struct score));
196
		TRYREALLOCARRAY(vscreen, (newrow - 1), sizeof(struct video *));
197
		TRYREALLOCARRAY(pscreen, (newrow - 1), sizeof(struct video *));
198
		TRYREALLOCARRAY(video, (newrow - 1), 2 * sizeof(struct video));
199
200
		/*
201
		 * Zero-out the entries we just allocated.
202
		 */
203
		for (i = vidstart; i < 2 * (newrow - 1); i++)
204
			memset(&video[i], 0, sizeof(struct video));
205
206
		/*
207
		 * Reinitialize vscreen and pscreen arrays completely.
208
		 */
209
		vp = &video[0];
210
		for (i = 0; i < newrow - 1; ++i) {
211
			vscreen[i] = vp;
212
			++vp;
213
			pscreen[i] = vp;
214
			++vp;
215
		}
216
	}
217
	if (rowchanged || colchanged || first_run) {
218
		for (i = 0; i < 2 * (newrow - 1); i++)
219
			TRYREALLOC(video[i].v_text, newcol);
220
		TRYREALLOC(blanks.v_text, newcol);
221
	}
222
223
	nrow = newrow;
224
	ncol = newcol;
225
226
	if (ttrow > nrow)
227
		ttrow = nrow;
228
	if (ttcol > ncol)
229
		ttcol = ncol;
230
231
	first_run = 0;
232
	return (TRUE);
233
}
234
235
#undef TRYREALLOC
236
#undef TRYREALLOCARRAY
237
238
/*
239
 * Initialize the data structures used
240
 * by the display code. The edge vectors used
241
 * to access the screens are set up. The operating
242
 * system's terminal I/O channel is set up. Fill the
243
 * "blanks" array with ASCII blanks. The rest is done
244
 * at compile time. The original window is marked
245
 * as needing full update, and the physical screen
246
 * is marked as garbage, so all the right stuff happens
247
 * on the first call to redisplay.
248
 */
249
void
250
vtinit(void)
251
{
252
	int	i;
253
254
	ttopen();
255
	ttinit();
256
257
	/*
258
	 * ttinit called ttresize(), which called vtresize(), so our data
259
	 * structures are setup correctly.
260
	 */
261
262
	blanks.v_color = CTEXT;
263
	for (i = 0; i < ncol; ++i)
264
		blanks.v_text[i] = ' ';
265
}
266
267
/*
268
 * Tidy up the virtual display system
269
 * in anticipation of a return back to the host
270
 * operating system. Right now all we do is position
271
 * the cursor to the last line, erase the line, and
272
 * close the terminal channel.
273
 */
274
void
275
vttidy(void)
276
{
277
	ttcolor(CTEXT);
278
	ttnowindow();		/* No scroll window.	 */
279
	ttmove(nrow - 1, 0);	/* Echo line.		 */
280
	tteeol();
281
	tttidy();
282
	ttflush();
283
	ttclose();
284
}
285
286
/*
287
 * Move the virtual cursor to an origin
288
 * 0 spot on the virtual display screen. I could
289
 * store the column as a character pointer to the spot
290
 * on the line, which would make "vtputc" a little bit
291
 * more efficient. No checking for errors.
292
 */
293
void
294
vtmove(int row, int col)
295
{
296
	vtrow = row;
297
	vtcol = col;
298
}
299
300
/*
301
 * Write a character to the virtual display,
302
 * dealing with long lines and the display of unprintable
303
 * things like control characters. Also expand tabs every 8
304
 * columns. This code only puts printing characters into
305
 * the virtual display image. Special care must be taken when
306
 * expanding tabs. On a screen whose width is not a multiple
307
 * of 8, it is possible for the virtual cursor to hit the
308
 * right margin before the next tab stop is reached. This
309
 * makes the tab code loop if you are not careful.
310
 * Three guesses how we found this.
311
 */
312
void
313
vtputc(int c)
314
{
315
	struct video	*vp;
316
317
	c &= 0xff;
318
319
	vp = vscreen[vtrow];
320
	if (vtcol >= ncol)
321
		vp->v_text[ncol - 1] = '$';
322
	else if (c == '\t'
323
#ifdef	NOTAB
324
	    && !(curbp->b_flag & BFNOTAB)
325
#endif
326
	    ) {
327
		do {
328
			vtputc(' ');
329
		} while (vtcol < ncol && (vtcol & 0x07) != 0);
330
	} else if (ISCTRL(c)) {
331
		vtputc('^');
332
		vtputc(CCHR(c));
333
	} else if (isprint(c))
334
		vp->v_text[vtcol++] = c;
335
	else {
336
		char bf[5];
337
338
		snprintf(bf, sizeof(bf), "\\%o", c);
339
		vtputs(bf);
340
	}
341
}
342
343
/*
344
 * Put a character to the virtual screen in an extended line.  If we are not
345
 * yet on left edge, don't print it yet.  Check for overflow on the right
346
 * margin.
347
 */
348
void
349
vtpute(int c)
350
{
351
	struct video *vp;
352
353
	c &= 0xff;
354
355
	vp = vscreen[vtrow];
356
	if (vtcol >= ncol)
357
		vp->v_text[ncol - 1] = '$';
358
	else if (c == '\t'
359
#ifdef	NOTAB
360
	    && !(curbp->b_flag & BFNOTAB)
361
#endif
362
	    ) {
363
		do {
364
			vtpute(' ');
365
		} while (((vtcol + lbound) & 0x07) != 0 && vtcol < ncol);
366
	} else if (ISCTRL(c) != FALSE) {
367
		vtpute('^');
368
		vtpute(CCHR(c));
369
	} else if (isprint(c)) {
370
		if (vtcol >= 0)
371
			vp->v_text[vtcol] = c;
372
		++vtcol;
373
	} else {
374
		char bf[5], *cp;
375
376
		snprintf(bf, sizeof(bf), "\\%o", c);
377
		for (cp = bf; *cp != '\0'; cp++)
378
			vtpute(*cp);
379
	}
380
}
381
382
/*
383
 * Erase from the end of the software cursor to the end of the line on which
384
 * the software cursor is located. The display routines will decide if a
385
 * hardware erase to end of line command should be used to display this.
386
 */
387
void
388
vteeol(void)
389
{
390
	struct video *vp;
391
392
	vp = vscreen[vtrow];
393
	while (vtcol < ncol)
394
		vp->v_text[vtcol++] = ' ';
395
}
396
397
/*
398
 * Make sure that the display is
399
 * right. This is a three part process. First,
400
 * scan through all of the windows looking for dirty
401
 * ones. Check the framing, and refresh the screen.
402
 * Second, make sure that "currow" and "curcol" are
403
 * correct for the current window. Third, make the
404
 * virtual and physical screens the same.
405
 */
406
void
407
update(int modelinecolor)
408
{
409
	struct line	*lp;
410
	struct mgwin	*wp;
411
	struct video	*vp1;
412
	struct video	*vp2;
413
	int	 c, i, j;
414
	int	 hflag;
415
	int	 currow, curcol;
416
	int	 offs, size;
417
418
	if (charswaiting())
419
		return;
420
	if (sgarbf) {		/* must update everything */
421
		wp = wheadp;
422
		while (wp != NULL) {
423
			wp->w_rflag |= WFMODE | WFFULL;
424
			wp = wp->w_wndp;
425
		}
426
	}
427
	if (linenos || colnos) {
428
		wp = wheadp;
429
		while (wp != NULL) {
430
			wp->w_rflag |= WFMODE;
431
			wp = wp->w_wndp;
432
		}
433
	}
434
	hflag = FALSE;			/* Not hard. */
435
	for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
436
		/*
437
		 * Nothing to be done.
438
		 */
439
		if (wp->w_rflag == 0)
440
			continue;
441
442
		if ((wp->w_rflag & WFFRAME) == 0) {
443
			lp = wp->w_linep;
444
			for (i = 0; i < wp->w_ntrows; ++i) {
445
				if (lp == wp->w_dotp)
446
					goto out;
447
				if (lp == wp->w_bufp->b_headp)
448
					break;
449
				lp = lforw(lp);
450
			}
451
		}
452
		/*
453
		 * Put the middle-line in place.
454
		 */
455
		i = wp->w_frame;
456
		if (i > 0) {
457
			--i;
458
			if (i >= wp->w_ntrows)
459
				i = wp->w_ntrows - 1;
460
		} else if (i < 0) {
461
			i += wp->w_ntrows;
462
			if (i < 0)
463
				i = 0;
464
		} else
465
			i = wp->w_ntrows / 2; /* current center, no change */
466
467
		/*
468
		 * Find the line.
469
		 */
470
		lp = wp->w_dotp;
471
		while (i != 0 && lback(lp) != wp->w_bufp->b_headp) {
472
			--i;
473
			lp = lback(lp);
474
		}
475
		wp->w_linep = lp;
476
		wp->w_rflag |= WFFULL;	/* Force full.		 */
477
	out:
478
		lp = wp->w_linep;	/* Try reduced update.	 */
479
		i = wp->w_toprow;
480
		if ((wp->w_rflag & ~WFMODE) == WFEDIT) {
481
			while (lp != wp->w_dotp) {
482
				++i;
483
				lp = lforw(lp);
484
			}
485
			vscreen[i]->v_color = CTEXT;
486
			vscreen[i]->v_flag |= (VFCHG | VFHBAD);
487
			vtmove(i, 0);
488
			for (j = 0; j < llength(lp); ++j)
489
				vtputc(lgetc(lp, j));
490
			vteeol();
491
		} else if ((wp->w_rflag & (WFEDIT | WFFULL)) != 0) {
492
			hflag = TRUE;
493
			while (i < wp->w_toprow + wp->w_ntrows) {
494
				vscreen[i]->v_color = CTEXT;
495
				vscreen[i]->v_flag |= (VFCHG | VFHBAD);
496
				vtmove(i, 0);
497
				if (lp != wp->w_bufp->b_headp) {
498
					for (j = 0; j < llength(lp); ++j)
499
						vtputc(lgetc(lp, j));
500
					lp = lforw(lp);
501
				}
502
				vteeol();
503
				++i;
504
			}
505
		}
506
		if ((wp->w_rflag & WFMODE) != 0)
507
			modeline(wp, modelinecolor);
508
		wp->w_rflag = 0;
509
		wp->w_frame = 0;
510
	}
511
	lp = curwp->w_linep;	/* Cursor location. */
512
	currow = curwp->w_toprow;
513
	while (lp != curwp->w_dotp) {
514
		++currow;
515
		lp = lforw(lp);
516
	}
517
	curcol = 0;
518
	i = 0;
519
	while (i < curwp->w_doto) {
520
		c = lgetc(lp, i++);
521
		if (c == '\t'
522
#ifdef	NOTAB
523
		    && !(curbp->b_flag & BFNOTAB)
524
#endif
525
			) {
526
			curcol |= 0x07;
527
			curcol++;
528
		} else if (ISCTRL(c) != FALSE)
529
			curcol += 2;
530
		else if (isprint(c))
531
			curcol++;
532
		else {
533
			char bf[5];
534
535
			snprintf(bf, sizeof(bf), "\\%o", c);
536
			curcol += strlen(bf);
537
		}
538
	}
539
	if (curcol >= ncol - 1) {	/* extended line. */
540
		/* flag we are extended and changed */
541
		vscreen[currow]->v_flag |= VFEXT | VFCHG;
542
		updext(currow, curcol);	/* and output extended line */
543
	} else
544
		lbound = 0;	/* not extended line */
545
546
	/*
547
	 * Make sure no lines need to be de-extended because the cursor is no
548
	 * longer on them.
549
	 */
550
	wp = wheadp;
551
	while (wp != NULL) {
552
		lp = wp->w_linep;
553
		i = wp->w_toprow;
554
		while (i < wp->w_toprow + wp->w_ntrows) {
555
			if (vscreen[i]->v_flag & VFEXT) {
556
				/* always flag extended lines as changed */
557
				vscreen[i]->v_flag |= VFCHG;
558
				if ((wp != curwp) || (lp != wp->w_dotp) ||
559
				    (curcol < ncol - 1)) {
560
					vtmove(i, 0);
561
					for (j = 0; j < llength(lp); ++j)
562
						vtputc(lgetc(lp, j));
563
					vteeol();
564
					/* this line no longer is extended */
565
					vscreen[i]->v_flag &= ~VFEXT;
566
				}
567
			}
568
			lp = lforw(lp);
569
			++i;
570
		}
571
		/* if garbaged then fix up mode lines */
572
		if (sgarbf != FALSE)
573
			vscreen[i]->v_flag |= VFCHG;
574
		/* and onward to the next window */
575
		wp = wp->w_wndp;
576
	}
577
578
	if (sgarbf != FALSE) {	/* Screen is garbage.	 */
579
		sgarbf = FALSE;	/* Erase-page clears.	 */
580
		epresf = FALSE;	/* The message area.	 */
581
		tttop = HUGE;	/* Forget where you set. */
582
		ttbot = HUGE;	/* scroll region.	 */
583
		tthue = CNONE;	/* Color unknown.	 */
584
		ttmove(0, 0);
585
		tteeop();
586
		for (i = 0; i < nrow - 1; ++i) {
587
			uline(i, vscreen[i], &blanks);
588
			ucopy(vscreen[i], pscreen[i]);
589
		}
590
		ttmove(currow, curcol - lbound);
591
		ttflush();
592
		return;
593
	}
594
	if (hflag != FALSE) {			/* Hard update?		*/
595
		for (i = 0; i < nrow - 1; ++i) {/* Compute hash data.	*/
596
			hash(vscreen[i]);
597
			hash(pscreen[i]);
598
		}
599
		offs = 0;			/* Get top match.	*/
600
		while (offs != nrow - 1) {
601
			vp1 = vscreen[offs];
602
			vp2 = pscreen[offs];
603
			if (vp1->v_color != vp2->v_color
604
			    || vp1->v_hash != vp2->v_hash)
605
				break;
606
			uline(offs, vp1, vp2);
607
			ucopy(vp1, vp2);
608
			++offs;
609
		}
610
		if (offs == nrow - 1) {		/* Might get it all.	*/
611
			ttmove(currow, curcol - lbound);
612
			ttflush();
613
			return;
614
		}
615
		size = nrow - 1;		/* Get bottom match.	*/
616
		while (size != offs) {
617
			vp1 = vscreen[size - 1];
618
			vp2 = pscreen[size - 1];
619
			if (vp1->v_color != vp2->v_color
620
			    || vp1->v_hash != vp2->v_hash)
621
				break;
622
			uline(size - 1, vp1, vp2);
623
			ucopy(vp1, vp2);
624
			--size;
625
		}
626
		if ((size -= offs) == 0)	/* Get screen size.	*/
627
			panic("Illegal screen size in update");
628
		setscores(offs, size);		/* Do hard update.	*/
629
		traceback(offs, size, size, size);
630
		for (i = 0; i < size; ++i)
631
			ucopy(vscreen[offs + i], pscreen[offs + i]);
632
		ttmove(currow, curcol - lbound);
633
		ttflush();
634
		return;
635
	}
636
	for (i = 0; i < nrow - 1; ++i) {	/* Easy update.		*/
637
		vp1 = vscreen[i];
638
		vp2 = pscreen[i];
639
		if ((vp1->v_flag & VFCHG) != 0) {
640
			uline(i, vp1, vp2);
641
			ucopy(vp1, vp2);
642
		}
643
	}
644
	ttmove(currow, curcol - lbound);
645
	ttflush();
646
}
647
648
/*
649
 * Update a saved copy of a line,
650
 * kept in a video structure. The "vvp" is
651
 * the one in the "vscreen". The "pvp" is the one
652
 * in the "pscreen". This is called to make the
653
 * virtual and physical screens the same when
654
 * display has done an update.
655
 */
656
void
657
ucopy(struct video *vvp, struct video *pvp)
658
{
659
	vvp->v_flag &= ~VFCHG;		/* Changes done.	 */
660
	pvp->v_flag = vvp->v_flag;	/* Update model.	 */
661
	pvp->v_hash = vvp->v_hash;
662
	pvp->v_cost = vvp->v_cost;
663
	pvp->v_color = vvp->v_color;
664
	bcopy(vvp->v_text, pvp->v_text, ncol);
665
}
666
667
/*
668
 * updext: update the extended line which the cursor is currently on at a
669
 * column greater than the terminal width. The line will be scrolled right or
670
 * left to let the user see where the cursor is.
671
 */
672
void
673
updext(int currow, int curcol)
674
{
675
	struct line	*lp;			/* pointer to current line */
676
	int	 j;			/* index into line */
677
678
	if (ncol < 2)
679
		return;
680
681
	/*
682
	 * calculate what column the left bound should be
683
	 * (force cursor into middle half of screen)
684
	 */
685
	lbound = curcol - (curcol % (ncol >> 1)) - (ncol >> 2);
686
687
	/*
688
	 * scan through the line outputing characters to the virtual screen
689
	 * once we reach the left edge
690
	 */
691
	vtmove(currow, -lbound);		/* start scanning offscreen */
692
	lp = curwp->w_dotp;			/* line to output */
693
	for (j = 0; j < llength(lp); ++j)	/* until the end-of-line */
694
		vtpute(lgetc(lp, j));
695
	vteeol();				/* truncate the virtual line */
696
	vscreen[currow]->v_text[0] = '$';	/* and put a '$' in column 1 */
697
}
698
699
/*
700
 * Update a single line. This routine only
701
 * uses basic functionality (no insert and delete character,
702
 * but erase to end of line). The "vvp" points at the video
703
 * structure for the line on the virtual screen, and the "pvp"
704
 * is the same for the physical screen. Avoid erase to end of
705
 * line when updating CMODE color lines, because of the way that
706
 * reverse video works on most terminals.
707
 */
708
void
709
uline(int row, struct video *vvp, struct video *pvp)
710
{
711
	char  *cp1;
712
	char  *cp2;
713
	char  *cp3;
714
	char  *cp4;
715
	char  *cp5;
716
	int    nbflag;
717
718
	if (vvp->v_color != pvp->v_color) {	/* Wrong color, do a	 */
719
		ttmove(row, 0);			/* full redraw.		 */
720
#ifdef	STANDOUT_GLITCH
721
		if (pvp->v_color != CTEXT && magic_cookie_glitch >= 0)
722
			tteeol();
723
#endif
724
		ttcolor(vvp->v_color);
725
#ifdef	STANDOUT_GLITCH
726
		cp1 = &vvp->v_text[magic_cookie_glitch > 0 ? magic_cookie_glitch : 0];
727
		/*
728
		 * The odd code for magic_cookie_glitch==0 is to avoid
729
		 * putting the invisible glitch character on the next line.
730
		 * (Hazeltine executive 80 model 30)
731
		 */
732
		cp2 = &vvp->v_text[ncol - (magic_cookie_glitch >= 0 ?
733
		    (magic_cookie_glitch != 0 ? magic_cookie_glitch : 1) : 0)];
734
#else
735
		cp1 = &vvp->v_text[0];
736
		cp2 = &vvp->v_text[ncol];
737
#endif
738
		while (cp1 != cp2) {
739
			ttputc(*cp1++);
740
			++ttcol;
741
		}
742
		ttcolor(CTEXT);
743
		return;
744
	}
745
	cp1 = &vvp->v_text[0];		/* Compute left match.	 */
746
	cp2 = &pvp->v_text[0];
747
	while (cp1 != &vvp->v_text[ncol] && cp1[0] == cp2[0]) {
748
		++cp1;
749
		++cp2;
750
	}
751
	if (cp1 == &vvp->v_text[ncol])	/* All equal.		 */
752
		return;
753
	nbflag = FALSE;
754
	cp3 = &vvp->v_text[ncol];	/* Compute right match.  */
755
	cp4 = &pvp->v_text[ncol];
756
	while (cp3[-1] == cp4[-1]) {
757
		--cp3;
758
		--cp4;
759
		if (cp3[0] != ' ')	/* Note non-blanks in	 */
760
			nbflag = TRUE;	/* the right match.	 */
761
	}
762
	cp5 = cp3;			/* Is erase good?	 */
763
	if (nbflag == FALSE && vvp->v_color == CTEXT) {
764
		while (cp5 != cp1 && cp5[-1] == ' ')
765
			--cp5;
766
		/* Alcyon hack */
767
		if ((int) (cp3 - cp5) <= tceeol)
768
			cp5 = cp3;
769
	}
770
	/* Alcyon hack */
771
	ttmove(row, (int) (cp1 - &vvp->v_text[0]));
772
#ifdef	STANDOUT_GLITCH
773
	if (vvp->v_color != CTEXT && magic_cookie_glitch > 0) {
774
		if (cp1 < &vvp->v_text[magic_cookie_glitch])
775
			cp1 = &vvp->v_text[magic_cookie_glitch];
776
		if (cp5 > &vvp->v_text[ncol - magic_cookie_glitch])
777
			cp5 = &vvp->v_text[ncol - magic_cookie_glitch];
778
	} else if (magic_cookie_glitch < 0)
779
#endif
780
		ttcolor(vvp->v_color);
781
	while (cp1 != cp5) {
782
		ttputc(*cp1++);
783
		++ttcol;
784
	}
785
	if (cp5 != cp3)			/* Do erase.		 */
786
		tteeol();
787
}
788
789
/*
790
 * Redisplay the mode line for the window pointed to by the "wp".
791
 * This is the only routine that has any idea of how the mode line is
792
 * formatted. You can change the modeline format by hacking at this
793
 * routine. Called by "update" any time there is a dirty window.  Note
794
 * that if STANDOUT_GLITCH is defined, first and last magic_cookie_glitch
795
 * characters may never be seen.
796
 */
797
void
798
modeline(struct mgwin *wp, int modelinecolor)
799
{
800
	int	n, md;
801
	struct buffer *bp;
802
	char sl[21];		/* Overkill. Space for 2^64 in base 10. */
803
	int len;
804
805
	n = wp->w_toprow + wp->w_ntrows;	/* Location.		 */
806
	vscreen[n]->v_color = modelinecolor;	/* Mode line color.	 */
807
	vscreen[n]->v_flag |= (VFCHG | VFHBAD);	/* Recompute, display.	 */
808
	vtmove(n, 0);				/* Seek to right line.	 */
809
	bp = wp->w_bufp;
810
	vtputc('-');
811
	vtputc('-');
812
	if ((bp->b_flag & BFREADONLY) != 0) {
813
		vtputc('%');
814
		if ((bp->b_flag & BFCHG) != 0)
815
			vtputc('*');
816
		else
817
			vtputc('%');
818
	} else if ((bp->b_flag & BFCHG) != 0) {	/* "*" if changed.	 */
819
		vtputc('*');
820
		vtputc('*');
821
	} else {
822
		vtputc('-');
823
		vtputc('-');
824
	}
825
	vtputc('-');
826
	n = 5;
827
	n += vtputs("Mg: ");
828
	if (bp->b_bname[0] != '\0')
829
		n += vtputs(&(bp->b_bname[0]));
830
	while (n < 42) {			/* Pad out with blanks.	 */
831
		vtputc(' ');
832
		++n;
833
	}
834
	vtputc('(');
835
	++n;
836
	for (md = 0; ; ) {
837
		n += vtputs(bp->b_modes[md]->p_name);
838
		if (++md > bp->b_nmodes)
839
			break;
840
		vtputc('-');
841
		++n;
842
	}
843
	/* XXX These should eventually move to a real mode */
844
	if (macrodef == TRUE)
845
		n += vtputs("-def");
846
	if (globalwd == TRUE)
847
		n += vtputs("-gwd");
848
	vtputc(')');
849
	++n;
850
851
	if (linenos && colnos)
852
		len = snprintf(sl, sizeof(sl), "--L%d--C%d", wp->w_dotline,
853
		    getcolpos(wp));
854
	else if (linenos)
855
		len = snprintf(sl, sizeof(sl), "--L%d", wp->w_dotline);
856
	else if (colnos)
857
		len = snprintf(sl, sizeof(sl), "--C%d", getcolpos(wp));
858
	if ((linenos || colnos) && len < sizeof(sl) && len != -1)
859
		n += vtputs(sl);
860
861
	while (n < ncol) {			/* Pad out.		 */
862
		vtputc('-');
863
		++n;
864
	}
865
}
866
867
/*
868
 * Output a string to the mode line, report how long it was.
869
 */
870
int
871
vtputs(const char *s)
872
{
873
	int n = 0;
874
875
	while (*s != '\0') {
876
		vtputc(*s++);
877
		++n;
878
	}
879
	return (n);
880
}
881
882
/*
883
 * Compute the hash code for the line pointed to by the "vp".
884
 * Recompute it if necessary. Also set the approximate redisplay
885
 * cost. The validity of the hash code is marked by a flag bit.
886
 * The cost understand the advantages of erase to end of line.
887
 * Tuned for the VAX by Bob McNamara; better than it used to be on
888
 * just about any machine.
889
 */
890
void
891
hash(struct video *vp)
892
{
893
	int	i, n;
894
	char   *s;
895
896
	if ((vp->v_flag & VFHBAD) != 0) {	/* Hash bad.		 */
897
		s = &vp->v_text[ncol - 1];
898
		for (i = ncol; i != 0; --i, --s)
899
			if (*s != ' ')
900
				break;
901
		n = ncol - i;			/* Erase cheaper?	 */
902
		if (n > tceeol)
903
			n = tceeol;
904
		vp->v_cost = i + n;		/* Bytes + blanks.	 */
905
		for (n = 0; i != 0; --i, --s)
906
			n = (n << 5) + n + *s;
907
		vp->v_hash = n;			/* Hash code.		 */
908
		vp->v_flag &= ~VFHBAD;		/* Flag as all done.	 */
909
	}
910
}
911
912
/*
913
 * Compute the Insert-Delete
914
 * cost matrix. The dynamic programming algorithm
915
 * described by James Gosling is used. This code assumes
916
 * that the line above the echo line is the last line involved
917
 * in the scroll region. This is easy to arrange on the VT100
918
 * because of the scrolling region. The "offs" is the origin 0
919
 * offset of the first row in the virtual/physical screen that
920
 * is being updated; the "size" is the length of the chunk of
921
 * screen being updated. For a full screen update, use offs=0
922
 * and size=nrow-1.
923
 *
924
 * Older versions of this code implemented the score matrix by
925
 * a two dimensional array of SCORE nodes. This put all kinds of
926
 * multiply instructions in the code! This version is written to
927
 * use a linear array and pointers, and contains no multiplication
928
 * at all. The code has been carefully looked at on the VAX, with
929
 * only marginal checking on other machines for efficiency. In
930
 * fact, this has been tuned twice! Bob McNamara tuned it even
931
 * more for the VAX, which is a big issue for him because of
932
 * the 66 line X displays.
933
 *
934
 * On some machines, replacing the "for (i=1; i<=size; ++i)" with
935
 * i = 1; do { } while (++i <=size)" will make the code quite a
936
 * bit better; but it looks ugly.
937
 */
938
void
939
setscores(int offs, int size)
940
{
941
	struct score	 *sp;
942
	struct score	 *sp1;
943
	struct video	**vp, **pp;
944
	struct video	**vbase, **pbase;
945
	int	  tempcost;
946
	int	  bestcost;
947
	int	  j, i;
948
949
	vbase = &vscreen[offs - 1];	/* By hand CSE's.	 */
950
	pbase = &pscreen[offs - 1];
951
	score[0].s_itrace = 0;		/* [0, 0]		 */
952
	score[0].s_jtrace = 0;
953
	score[0].s_cost = 0;
954
	sp = &score[1];			/* Row 0, inserts.	 */
955
	tempcost = 0;
956
	vp = &vbase[1];
957
	for (j = 1; j <= size; ++j) {
958
		sp->s_itrace = 0;
959
		sp->s_jtrace = j - 1;
960
		tempcost += tcinsl;
961
		tempcost += (*vp)->v_cost;
962
		sp->s_cost = tempcost;
963
		++vp;
964
		++sp;
965
	}
966
	sp = &score[nrow];		/* Column 0, deletes.	 */
967
	tempcost = 0;
968
	for (i = 1; i <= size; ++i) {
969
		sp->s_itrace = i - 1;
970
		sp->s_jtrace = 0;
971
		tempcost += tcdell;
972
		sp->s_cost = tempcost;
973
		sp += nrow;
974
	}
975
	sp1 = &score[nrow + 1];		/* [1, 1].		 */
976
	pp = &pbase[1];
977
	for (i = 1; i <= size; ++i) {
978
		sp = sp1;
979
		vp = &vbase[1];
980
		for (j = 1; j <= size; ++j) {
981
			sp->s_itrace = i - 1;
982
			sp->s_jtrace = j;
983
			bestcost = (sp - nrow)->s_cost;
984
			if (j != size)	/* Cd(A[i])=0 @ Dis.	 */
985
				bestcost += tcdell;
986
			tempcost = (sp - 1)->s_cost;
987
			tempcost += (*vp)->v_cost;
988
			if (i != size)	/* Ci(B[j])=0 @ Dsj.	 */
989
				tempcost += tcinsl;
990
			if (tempcost < bestcost) {
991
				sp->s_itrace = i;
992
				sp->s_jtrace = j - 1;
993
				bestcost = tempcost;
994
			}
995
			tempcost = (sp - nrow - 1)->s_cost;
996
			if ((*pp)->v_color != (*vp)->v_color
997
			    || (*pp)->v_hash != (*vp)->v_hash)
998
				tempcost += (*vp)->v_cost;
999
			if (tempcost < bestcost) {
1000
				sp->s_itrace = i - 1;
1001
				sp->s_jtrace = j - 1;
1002
				bestcost = tempcost;
1003
			}
1004
			sp->s_cost = bestcost;
1005
			++sp;		/* Next column.		 */
1006
			++vp;
1007
		}
1008
		++pp;
1009
		sp1 += nrow;		/* Next row.		 */
1010
	}
1011
}
1012
1013
/*
1014
 * Trace back through the dynamic programming cost
1015
 * matrix, and update the screen using an optimal sequence
1016
 * of redraws, insert lines, and delete lines. The "offs" is
1017
 * the origin 0 offset of the chunk of the screen we are about to
1018
 * update. The "i" and "j" are always started in the lower right
1019
 * corner of the matrix, and imply the size of the screen.
1020
 * A full screen traceback is called with offs=0 and i=j=nrow-1.
1021
 * There is some do-it-yourself double subscripting here,
1022
 * which is acceptable because this routine is much less compute
1023
 * intensive then the code that builds the score matrix!
1024
 */
1025
void
1026
traceback(int offs, int size, int i, int j)
1027
{
1028
	int	itrace, jtrace;
1029
	int	k;
1030
	int	ninsl, ndraw, ndell;
1031
1032
	if (i == 0 && j == 0)	/* End of update.	 */
1033
		return;
1034
	itrace = score[(nrow * i) + j].s_itrace;
1035
	jtrace = score[(nrow * i) + j].s_jtrace;
1036
	if (itrace == i) {	/* [i, j-1]		 */
1037
		ninsl = 0;	/* Collect inserts.	 */
1038
		if (i != size)
1039
			ninsl = 1;
1040
		ndraw = 1;
1041
		while (itrace != 0 || jtrace != 0) {
1042
			if (score[(nrow * itrace) + jtrace].s_itrace != itrace)
1043
				break;
1044
			jtrace = score[(nrow * itrace) + jtrace].s_jtrace;
1045
			if (i != size)
1046
				++ninsl;
1047
			++ndraw;
1048
		}
1049
		traceback(offs, size, itrace, jtrace);
1050
		if (ninsl != 0) {
1051
			ttcolor(CTEXT);
1052
			ttinsl(offs + j - ninsl, offs + size - 1, ninsl);
1053
		}
1054
		do {		/* B[j], A[j] blank.	 */
1055
			k = offs + j - ndraw;
1056
			uline(k, vscreen[k], &blanks);
1057
		} while (--ndraw);
1058
		return;
1059
	}
1060
	if (jtrace == j) {	/* [i-1, j]		 */
1061
		ndell = 0;	/* Collect deletes.	 */
1062
		if (j != size)
1063
			ndell = 1;
1064
		while (itrace != 0 || jtrace != 0) {
1065
			if (score[(nrow * itrace) + jtrace].s_jtrace != jtrace)
1066
				break;
1067
			itrace = score[(nrow * itrace) + jtrace].s_itrace;
1068
			if (j != size)
1069
				++ndell;
1070
		}
1071
		if (ndell != 0) {
1072
			ttcolor(CTEXT);
1073
			ttdell(offs + i - ndell, offs + size - 1, ndell);
1074
		}
1075
		traceback(offs, size, itrace, jtrace);
1076
		return;
1077
	}
1078
	traceback(offs, size, itrace, jtrace);
1079
	k = offs + j - 1;
1080
	uline(k, vscreen[k], pscreen[offs + i - 1]);
1081
}