1 |
|
|
/* $OpenBSD: display.c,v 1.47 2015/04/03 22:10:29 bcallah 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 { |
370 |
|
|
if (vtcol >= 0) |
371 |
|
|
vp->v_text[vtcol] = c; |
372 |
|
|
++vtcol; |
373 |
|
|
} |
374 |
|
|
} |
375 |
|
|
|
376 |
|
|
/* |
377 |
|
|
* Erase from the end of the software cursor to the end of the line on which |
378 |
|
|
* the software cursor is located. The display routines will decide if a |
379 |
|
|
* hardware erase to end of line command should be used to display this. |
380 |
|
|
*/ |
381 |
|
|
void |
382 |
|
|
vteeol(void) |
383 |
|
|
{ |
384 |
|
|
struct video *vp; |
385 |
|
|
|
386 |
|
|
vp = vscreen[vtrow]; |
387 |
|
|
while (vtcol < ncol) |
388 |
|
|
vp->v_text[vtcol++] = ' '; |
389 |
|
|
} |
390 |
|
|
|
391 |
|
|
/* |
392 |
|
|
* Make sure that the display is |
393 |
|
|
* right. This is a three part process. First, |
394 |
|
|
* scan through all of the windows looking for dirty |
395 |
|
|
* ones. Check the framing, and refresh the screen. |
396 |
|
|
* Second, make sure that "currow" and "curcol" are |
397 |
|
|
* correct for the current window. Third, make the |
398 |
|
|
* virtual and physical screens the same. |
399 |
|
|
*/ |
400 |
|
|
void |
401 |
|
|
update(int modelinecolor) |
402 |
|
|
{ |
403 |
|
|
struct line *lp; |
404 |
|
|
struct mgwin *wp; |
405 |
|
|
struct video *vp1; |
406 |
|
|
struct video *vp2; |
407 |
|
|
int c, i, j; |
408 |
|
|
int hflag; |
409 |
|
|
int currow, curcol; |
410 |
|
|
int offs, size; |
411 |
|
|
|
412 |
|
|
if (charswaiting()) |
413 |
|
|
return; |
414 |
|
|
if (sgarbf) { /* must update everything */ |
415 |
|
|
wp = wheadp; |
416 |
|
|
while (wp != NULL) { |
417 |
|
|
wp->w_rflag |= WFMODE | WFFULL; |
418 |
|
|
wp = wp->w_wndp; |
419 |
|
|
} |
420 |
|
|
} |
421 |
|
|
if (linenos || colnos) { |
422 |
|
|
wp = wheadp; |
423 |
|
|
while (wp != NULL) { |
424 |
|
|
wp->w_rflag |= WFMODE; |
425 |
|
|
wp = wp->w_wndp; |
426 |
|
|
} |
427 |
|
|
} |
428 |
|
|
hflag = FALSE; /* Not hard. */ |
429 |
|
|
for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { |
430 |
|
|
/* |
431 |
|
|
* Nothing to be done. |
432 |
|
|
*/ |
433 |
|
|
if (wp->w_rflag == 0) |
434 |
|
|
continue; |
435 |
|
|
|
436 |
|
|
if ((wp->w_rflag & WFFRAME) == 0) { |
437 |
|
|
lp = wp->w_linep; |
438 |
|
|
for (i = 0; i < wp->w_ntrows; ++i) { |
439 |
|
|
if (lp == wp->w_dotp) |
440 |
|
|
goto out; |
441 |
|
|
if (lp == wp->w_bufp->b_headp) |
442 |
|
|
break; |
443 |
|
|
lp = lforw(lp); |
444 |
|
|
} |
445 |
|
|
} |
446 |
|
|
/* |
447 |
|
|
* Put the middle-line in place. |
448 |
|
|
*/ |
449 |
|
|
i = wp->w_frame; |
450 |
|
|
if (i > 0) { |
451 |
|
|
--i; |
452 |
|
|
if (i >= wp->w_ntrows) |
453 |
|
|
i = wp->w_ntrows - 1; |
454 |
|
|
} else if (i < 0) { |
455 |
|
|
i += wp->w_ntrows; |
456 |
|
|
if (i < 0) |
457 |
|
|
i = 0; |
458 |
|
|
} else |
459 |
|
|
i = wp->w_ntrows / 2; /* current center, no change */ |
460 |
|
|
|
461 |
|
|
/* |
462 |
|
|
* Find the line. |
463 |
|
|
*/ |
464 |
|
|
lp = wp->w_dotp; |
465 |
|
|
while (i != 0 && lback(lp) != wp->w_bufp->b_headp) { |
466 |
|
|
--i; |
467 |
|
|
lp = lback(lp); |
468 |
|
|
} |
469 |
|
|
wp->w_linep = lp; |
470 |
|
|
wp->w_rflag |= WFFULL; /* Force full. */ |
471 |
|
|
out: |
472 |
|
|
lp = wp->w_linep; /* Try reduced update. */ |
473 |
|
|
i = wp->w_toprow; |
474 |
|
|
if ((wp->w_rflag & ~WFMODE) == WFEDIT) { |
475 |
|
|
while (lp != wp->w_dotp) { |
476 |
|
|
++i; |
477 |
|
|
lp = lforw(lp); |
478 |
|
|
} |
479 |
|
|
vscreen[i]->v_color = CTEXT; |
480 |
|
|
vscreen[i]->v_flag |= (VFCHG | VFHBAD); |
481 |
|
|
vtmove(i, 0); |
482 |
|
|
for (j = 0; j < llength(lp); ++j) |
483 |
|
|
vtputc(lgetc(lp, j)); |
484 |
|
|
vteeol(); |
485 |
|
|
} else if ((wp->w_rflag & (WFEDIT | WFFULL)) != 0) { |
486 |
|
|
hflag = TRUE; |
487 |
|
|
while (i < wp->w_toprow + wp->w_ntrows) { |
488 |
|
|
vscreen[i]->v_color = CTEXT; |
489 |
|
|
vscreen[i]->v_flag |= (VFCHG | VFHBAD); |
490 |
|
|
vtmove(i, 0); |
491 |
|
|
if (lp != wp->w_bufp->b_headp) { |
492 |
|
|
for (j = 0; j < llength(lp); ++j) |
493 |
|
|
vtputc(lgetc(lp, j)); |
494 |
|
|
lp = lforw(lp); |
495 |
|
|
} |
496 |
|
|
vteeol(); |
497 |
|
|
++i; |
498 |
|
|
} |
499 |
|
|
} |
500 |
|
|
if ((wp->w_rflag & WFMODE) != 0) |
501 |
|
|
modeline(wp, modelinecolor); |
502 |
|
|
wp->w_rflag = 0; |
503 |
|
|
wp->w_frame = 0; |
504 |
|
|
} |
505 |
|
|
lp = curwp->w_linep; /* Cursor location. */ |
506 |
|
|
currow = curwp->w_toprow; |
507 |
|
|
while (lp != curwp->w_dotp) { |
508 |
|
|
++currow; |
509 |
|
|
lp = lforw(lp); |
510 |
|
|
} |
511 |
|
|
curcol = 0; |
512 |
|
|
i = 0; |
513 |
|
|
while (i < curwp->w_doto) { |
514 |
|
|
c = lgetc(lp, i++); |
515 |
|
|
if (c == '\t' |
516 |
|
|
#ifdef NOTAB |
517 |
|
|
&& !(curbp->b_flag & BFNOTAB) |
518 |
|
|
#endif |
519 |
|
|
) { |
520 |
|
|
curcol |= 0x07; |
521 |
|
|
curcol++; |
522 |
|
|
} else if (ISCTRL(c) != FALSE) |
523 |
|
|
curcol += 2; |
524 |
|
|
else if (isprint(c)) |
525 |
|
|
curcol++; |
526 |
|
|
else { |
527 |
|
|
char bf[5]; |
528 |
|
|
|
529 |
|
|
snprintf(bf, sizeof(bf), "\\%o", c); |
530 |
|
|
curcol += strlen(bf); |
531 |
|
|
} |
532 |
|
|
} |
533 |
|
|
if (curcol >= ncol - 1) { /* extended line. */ |
534 |
|
|
/* flag we are extended and changed */ |
535 |
|
|
vscreen[currow]->v_flag |= VFEXT | VFCHG; |
536 |
|
|
updext(currow, curcol); /* and output extended line */ |
537 |
|
|
} else |
538 |
|
|
lbound = 0; /* not extended line */ |
539 |
|
|
|
540 |
|
|
/* |
541 |
|
|
* Make sure no lines need to be de-extended because the cursor is no |
542 |
|
|
* longer on them. |
543 |
|
|
*/ |
544 |
|
|
wp = wheadp; |
545 |
|
|
while (wp != NULL) { |
546 |
|
|
lp = wp->w_linep; |
547 |
|
|
i = wp->w_toprow; |
548 |
|
|
while (i < wp->w_toprow + wp->w_ntrows) { |
549 |
|
|
if (vscreen[i]->v_flag & VFEXT) { |
550 |
|
|
/* always flag extended lines as changed */ |
551 |
|
|
vscreen[i]->v_flag |= VFCHG; |
552 |
|
|
if ((wp != curwp) || (lp != wp->w_dotp) || |
553 |
|
|
(curcol < ncol - 1)) { |
554 |
|
|
vtmove(i, 0); |
555 |
|
|
for (j = 0; j < llength(lp); ++j) |
556 |
|
|
vtputc(lgetc(lp, j)); |
557 |
|
|
vteeol(); |
558 |
|
|
/* this line no longer is extended */ |
559 |
|
|
vscreen[i]->v_flag &= ~VFEXT; |
560 |
|
|
} |
561 |
|
|
} |
562 |
|
|
lp = lforw(lp); |
563 |
|
|
++i; |
564 |
|
|
} |
565 |
|
|
/* if garbaged then fix up mode lines */ |
566 |
|
|
if (sgarbf != FALSE) |
567 |
|
|
vscreen[i]->v_flag |= VFCHG; |
568 |
|
|
/* and onward to the next window */ |
569 |
|
|
wp = wp->w_wndp; |
570 |
|
|
} |
571 |
|
|
|
572 |
|
|
if (sgarbf != FALSE) { /* Screen is garbage. */ |
573 |
|
|
sgarbf = FALSE; /* Erase-page clears. */ |
574 |
|
|
epresf = FALSE; /* The message area. */ |
575 |
|
|
tttop = HUGE; /* Forget where you set. */ |
576 |
|
|
ttbot = HUGE; /* scroll region. */ |
577 |
|
|
tthue = CNONE; /* Color unknown. */ |
578 |
|
|
ttmove(0, 0); |
579 |
|
|
tteeop(); |
580 |
|
|
for (i = 0; i < nrow - 1; ++i) { |
581 |
|
|
uline(i, vscreen[i], &blanks); |
582 |
|
|
ucopy(vscreen[i], pscreen[i]); |
583 |
|
|
} |
584 |
|
|
ttmove(currow, curcol - lbound); |
585 |
|
|
ttflush(); |
586 |
|
|
return; |
587 |
|
|
} |
588 |
|
|
if (hflag != FALSE) { /* Hard update? */ |
589 |
|
|
for (i = 0; i < nrow - 1; ++i) {/* Compute hash data. */ |
590 |
|
|
hash(vscreen[i]); |
591 |
|
|
hash(pscreen[i]); |
592 |
|
|
} |
593 |
|
|
offs = 0; /* Get top match. */ |
594 |
|
|
while (offs != nrow - 1) { |
595 |
|
|
vp1 = vscreen[offs]; |
596 |
|
|
vp2 = pscreen[offs]; |
597 |
|
|
if (vp1->v_color != vp2->v_color |
598 |
|
|
|| vp1->v_hash != vp2->v_hash) |
599 |
|
|
break; |
600 |
|
|
uline(offs, vp1, vp2); |
601 |
|
|
ucopy(vp1, vp2); |
602 |
|
|
++offs; |
603 |
|
|
} |
604 |
|
|
if (offs == nrow - 1) { /* Might get it all. */ |
605 |
|
|
ttmove(currow, curcol - lbound); |
606 |
|
|
ttflush(); |
607 |
|
|
return; |
608 |
|
|
} |
609 |
|
|
size = nrow - 1; /* Get bottom match. */ |
610 |
|
|
while (size != offs) { |
611 |
|
|
vp1 = vscreen[size - 1]; |
612 |
|
|
vp2 = pscreen[size - 1]; |
613 |
|
|
if (vp1->v_color != vp2->v_color |
614 |
|
|
|| vp1->v_hash != vp2->v_hash) |
615 |
|
|
break; |
616 |
|
|
uline(size - 1, vp1, vp2); |
617 |
|
|
ucopy(vp1, vp2); |
618 |
|
|
--size; |
619 |
|
|
} |
620 |
|
|
if ((size -= offs) == 0) /* Get screen size. */ |
621 |
|
|
panic("Illegal screen size in update"); |
622 |
|
|
setscores(offs, size); /* Do hard update. */ |
623 |
|
|
traceback(offs, size, size, size); |
624 |
|
|
for (i = 0; i < size; ++i) |
625 |
|
|
ucopy(vscreen[offs + i], pscreen[offs + i]); |
626 |
|
|
ttmove(currow, curcol - lbound); |
627 |
|
|
ttflush(); |
628 |
|
|
return; |
629 |
|
|
} |
630 |
|
|
for (i = 0; i < nrow - 1; ++i) { /* Easy update. */ |
631 |
|
|
vp1 = vscreen[i]; |
632 |
|
|
vp2 = pscreen[i]; |
633 |
|
|
if ((vp1->v_flag & VFCHG) != 0) { |
634 |
|
|
uline(i, vp1, vp2); |
635 |
|
|
ucopy(vp1, vp2); |
636 |
|
|
} |
637 |
|
|
} |
638 |
|
|
ttmove(currow, curcol - lbound); |
639 |
|
|
ttflush(); |
640 |
|
|
} |
641 |
|
|
|
642 |
|
|
/* |
643 |
|
|
* Update a saved copy of a line, |
644 |
|
|
* kept in a video structure. The "vvp" is |
645 |
|
|
* the one in the "vscreen". The "pvp" is the one |
646 |
|
|
* in the "pscreen". This is called to make the |
647 |
|
|
* virtual and physical screens the same when |
648 |
|
|
* display has done an update. |
649 |
|
|
*/ |
650 |
|
|
void |
651 |
|
|
ucopy(struct video *vvp, struct video *pvp) |
652 |
|
|
{ |
653 |
|
|
vvp->v_flag &= ~VFCHG; /* Changes done. */ |
654 |
|
|
pvp->v_flag = vvp->v_flag; /* Update model. */ |
655 |
|
|
pvp->v_hash = vvp->v_hash; |
656 |
|
|
pvp->v_cost = vvp->v_cost; |
657 |
|
|
pvp->v_color = vvp->v_color; |
658 |
|
|
bcopy(vvp->v_text, pvp->v_text, ncol); |
659 |
|
|
} |
660 |
|
|
|
661 |
|
|
/* |
662 |
|
|
* updext: update the extended line which the cursor is currently on at a |
663 |
|
|
* column greater than the terminal width. The line will be scrolled right or |
664 |
|
|
* left to let the user see where the cursor is. |
665 |
|
|
*/ |
666 |
|
|
void |
667 |
|
|
updext(int currow, int curcol) |
668 |
|
|
{ |
669 |
|
|
struct line *lp; /* pointer to current line */ |
670 |
|
|
int j; /* index into line */ |
671 |
|
|
|
672 |
|
|
if (ncol < 2) |
673 |
|
|
return; |
674 |
|
|
|
675 |
|
|
/* |
676 |
|
|
* calculate what column the left bound should be |
677 |
|
|
* (force cursor into middle half of screen) |
678 |
|
|
*/ |
679 |
|
|
lbound = curcol - (curcol % (ncol >> 1)) - (ncol >> 2); |
680 |
|
|
|
681 |
|
|
/* |
682 |
|
|
* scan through the line outputing characters to the virtual screen |
683 |
|
|
* once we reach the left edge |
684 |
|
|
*/ |
685 |
|
|
vtmove(currow, -lbound); /* start scanning offscreen */ |
686 |
|
|
lp = curwp->w_dotp; /* line to output */ |
687 |
|
|
for (j = 0; j < llength(lp); ++j) /* until the end-of-line */ |
688 |
|
|
vtpute(lgetc(lp, j)); |
689 |
|
|
vteeol(); /* truncate the virtual line */ |
690 |
|
|
vscreen[currow]->v_text[0] = '$'; /* and put a '$' in column 1 */ |
691 |
|
|
} |
692 |
|
|
|
693 |
|
|
/* |
694 |
|
|
* Update a single line. This routine only |
695 |
|
|
* uses basic functionality (no insert and delete character, |
696 |
|
|
* but erase to end of line). The "vvp" points at the video |
697 |
|
|
* structure for the line on the virtual screen, and the "pvp" |
698 |
|
|
* is the same for the physical screen. Avoid erase to end of |
699 |
|
|
* line when updating CMODE color lines, because of the way that |
700 |
|
|
* reverse video works on most terminals. |
701 |
|
|
*/ |
702 |
|
|
void |
703 |
|
|
uline(int row, struct video *vvp, struct video *pvp) |
704 |
|
|
{ |
705 |
|
|
char *cp1; |
706 |
|
|
char *cp2; |
707 |
|
|
char *cp3; |
708 |
|
|
char *cp4; |
709 |
|
|
char *cp5; |
710 |
|
|
int nbflag; |
711 |
|
|
|
712 |
|
|
if (vvp->v_color != pvp->v_color) { /* Wrong color, do a */ |
713 |
|
|
ttmove(row, 0); /* full redraw. */ |
714 |
|
|
#ifdef STANDOUT_GLITCH |
715 |
|
|
if (pvp->v_color != CTEXT && magic_cookie_glitch >= 0) |
716 |
|
|
tteeol(); |
717 |
|
|
#endif |
718 |
|
|
ttcolor(vvp->v_color); |
719 |
|
|
#ifdef STANDOUT_GLITCH |
720 |
|
|
cp1 = &vvp->v_text[magic_cookie_glitch > 0 ? magic_cookie_glitch : 0]; |
721 |
|
|
/* |
722 |
|
|
* The odd code for magic_cookie_glitch==0 is to avoid |
723 |
|
|
* putting the invisible glitch character on the next line. |
724 |
|
|
* (Hazeltine executive 80 model 30) |
725 |
|
|
*/ |
726 |
|
|
cp2 = &vvp->v_text[ncol - (magic_cookie_glitch >= 0 ? |
727 |
|
|
(magic_cookie_glitch != 0 ? magic_cookie_glitch : 1) : 0)]; |
728 |
|
|
#else |
729 |
|
|
cp1 = &vvp->v_text[0]; |
730 |
|
|
cp2 = &vvp->v_text[ncol]; |
731 |
|
|
#endif |
732 |
|
|
while (cp1 != cp2) { |
733 |
|
|
ttputc(*cp1++); |
734 |
|
|
++ttcol; |
735 |
|
|
} |
736 |
|
|
ttcolor(CTEXT); |
737 |
|
|
return; |
738 |
|
|
} |
739 |
|
|
cp1 = &vvp->v_text[0]; /* Compute left match. */ |
740 |
|
|
cp2 = &pvp->v_text[0]; |
741 |
|
|
while (cp1 != &vvp->v_text[ncol] && cp1[0] == cp2[0]) { |
742 |
|
|
++cp1; |
743 |
|
|
++cp2; |
744 |
|
|
} |
745 |
|
|
if (cp1 == &vvp->v_text[ncol]) /* All equal. */ |
746 |
|
|
return; |
747 |
|
|
nbflag = FALSE; |
748 |
|
|
cp3 = &vvp->v_text[ncol]; /* Compute right match. */ |
749 |
|
|
cp4 = &pvp->v_text[ncol]; |
750 |
|
|
while (cp3[-1] == cp4[-1]) { |
751 |
|
|
--cp3; |
752 |
|
|
--cp4; |
753 |
|
|
if (cp3[0] != ' ') /* Note non-blanks in */ |
754 |
|
|
nbflag = TRUE; /* the right match. */ |
755 |
|
|
} |
756 |
|
|
cp5 = cp3; /* Is erase good? */ |
757 |
|
|
if (nbflag == FALSE && vvp->v_color == CTEXT) { |
758 |
|
|
while (cp5 != cp1 && cp5[-1] == ' ') |
759 |
|
|
--cp5; |
760 |
|
|
/* Alcyon hack */ |
761 |
|
|
if ((int) (cp3 - cp5) <= tceeol) |
762 |
|
|
cp5 = cp3; |
763 |
|
|
} |
764 |
|
|
/* Alcyon hack */ |
765 |
|
|
ttmove(row, (int) (cp1 - &vvp->v_text[0])); |
766 |
|
|
#ifdef STANDOUT_GLITCH |
767 |
|
|
if (vvp->v_color != CTEXT && magic_cookie_glitch > 0) { |
768 |
|
|
if (cp1 < &vvp->v_text[magic_cookie_glitch]) |
769 |
|
|
cp1 = &vvp->v_text[magic_cookie_glitch]; |
770 |
|
|
if (cp5 > &vvp->v_text[ncol - magic_cookie_glitch]) |
771 |
|
|
cp5 = &vvp->v_text[ncol - magic_cookie_glitch]; |
772 |
|
|
} else if (magic_cookie_glitch < 0) |
773 |
|
|
#endif |
774 |
|
|
ttcolor(vvp->v_color); |
775 |
|
|
while (cp1 != cp5) { |
776 |
|
|
ttputc(*cp1++); |
777 |
|
|
++ttcol; |
778 |
|
|
} |
779 |
|
|
if (cp5 != cp3) /* Do erase. */ |
780 |
|
|
tteeol(); |
781 |
|
|
} |
782 |
|
|
|
783 |
|
|
/* |
784 |
|
|
* Redisplay the mode line for the window pointed to by the "wp". |
785 |
|
|
* This is the only routine that has any idea of how the mode line is |
786 |
|
|
* formatted. You can change the modeline format by hacking at this |
787 |
|
|
* routine. Called by "update" any time there is a dirty window. Note |
788 |
|
|
* that if STANDOUT_GLITCH is defined, first and last magic_cookie_glitch |
789 |
|
|
* characters may never be seen. |
790 |
|
|
*/ |
791 |
|
|
void |
792 |
|
|
modeline(struct mgwin *wp, int modelinecolor) |
793 |
|
|
{ |
794 |
|
|
int n, md; |
795 |
|
|
struct buffer *bp; |
796 |
|
|
char sl[21]; /* Overkill. Space for 2^64 in base 10. */ |
797 |
|
|
int len; |
798 |
|
|
|
799 |
|
|
n = wp->w_toprow + wp->w_ntrows; /* Location. */ |
800 |
|
|
vscreen[n]->v_color = modelinecolor; /* Mode line color. */ |
801 |
|
|
vscreen[n]->v_flag |= (VFCHG | VFHBAD); /* Recompute, display. */ |
802 |
|
|
vtmove(n, 0); /* Seek to right line. */ |
803 |
|
|
bp = wp->w_bufp; |
804 |
|
|
vtputc('-'); |
805 |
|
|
vtputc('-'); |
806 |
|
|
if ((bp->b_flag & BFREADONLY) != 0) { |
807 |
|
|
vtputc('%'); |
808 |
|
|
if ((bp->b_flag & BFCHG) != 0) |
809 |
|
|
vtputc('*'); |
810 |
|
|
else |
811 |
|
|
vtputc('%'); |
812 |
|
|
} else if ((bp->b_flag & BFCHG) != 0) { /* "*" if changed. */ |
813 |
|
|
vtputc('*'); |
814 |
|
|
vtputc('*'); |
815 |
|
|
} else { |
816 |
|
|
vtputc('-'); |
817 |
|
|
vtputc('-'); |
818 |
|
|
} |
819 |
|
|
vtputc('-'); |
820 |
|
|
n = 5; |
821 |
|
|
n += vtputs("Mg: "); |
822 |
|
|
if (bp->b_bname[0] != '\0') |
823 |
|
|
n += vtputs(&(bp->b_bname[0])); |
824 |
|
|
while (n < 42) { /* Pad out with blanks. */ |
825 |
|
|
vtputc(' '); |
826 |
|
|
++n; |
827 |
|
|
} |
828 |
|
|
vtputc('('); |
829 |
|
|
++n; |
830 |
|
|
for (md = 0; ; ) { |
831 |
|
|
n += vtputs(bp->b_modes[md]->p_name); |
832 |
|
|
if (++md > bp->b_nmodes) |
833 |
|
|
break; |
834 |
|
|
vtputc('-'); |
835 |
|
|
++n; |
836 |
|
|
} |
837 |
|
|
/* XXX These should eventually move to a real mode */ |
838 |
|
|
if (macrodef == TRUE) |
839 |
|
|
n += vtputs("-def"); |
840 |
|
|
if (globalwd == TRUE) |
841 |
|
|
n += vtputs("-gwd"); |
842 |
|
|
vtputc(')'); |
843 |
|
|
++n; |
844 |
|
|
|
845 |
|
|
if (linenos && colnos) |
846 |
|
|
len = snprintf(sl, sizeof(sl), "--L%d--C%d", wp->w_dotline, |
847 |
|
|
getcolpos(wp)); |
848 |
|
|
else if (linenos) |
849 |
|
|
len = snprintf(sl, sizeof(sl), "--L%d", wp->w_dotline); |
850 |
|
|
else if (colnos) |
851 |
|
|
len = snprintf(sl, sizeof(sl), "--C%d", getcolpos(wp)); |
852 |
|
|
if ((linenos || colnos) && len < sizeof(sl) && len != -1) |
853 |
|
|
n += vtputs(sl); |
854 |
|
|
|
855 |
|
|
while (n < ncol) { /* Pad out. */ |
856 |
|
|
vtputc('-'); |
857 |
|
|
++n; |
858 |
|
|
} |
859 |
|
|
} |
860 |
|
|
|
861 |
|
|
/* |
862 |
|
|
* Output a string to the mode line, report how long it was. |
863 |
|
|
*/ |
864 |
|
|
int |
865 |
|
|
vtputs(const char *s) |
866 |
|
|
{ |
867 |
|
|
int n = 0; |
868 |
|
|
|
869 |
|
|
while (*s != '\0') { |
870 |
|
|
vtputc(*s++); |
871 |
|
|
++n; |
872 |
|
|
} |
873 |
|
|
return (n); |
874 |
|
|
} |
875 |
|
|
|
876 |
|
|
/* |
877 |
|
|
* Compute the hash code for the line pointed to by the "vp". |
878 |
|
|
* Recompute it if necessary. Also set the approximate redisplay |
879 |
|
|
* cost. The validity of the hash code is marked by a flag bit. |
880 |
|
|
* The cost understand the advantages of erase to end of line. |
881 |
|
|
* Tuned for the VAX by Bob McNamara; better than it used to be on |
882 |
|
|
* just about any machine. |
883 |
|
|
*/ |
884 |
|
|
void |
885 |
|
|
hash(struct video *vp) |
886 |
|
|
{ |
887 |
|
|
int i, n; |
888 |
|
|
char *s; |
889 |
|
|
|
890 |
|
|
if ((vp->v_flag & VFHBAD) != 0) { /* Hash bad. */ |
891 |
|
|
s = &vp->v_text[ncol - 1]; |
892 |
|
|
for (i = ncol; i != 0; --i, --s) |
893 |
|
|
if (*s != ' ') |
894 |
|
|
break; |
895 |
|
|
n = ncol - i; /* Erase cheaper? */ |
896 |
|
|
if (n > tceeol) |
897 |
|
|
n = tceeol; |
898 |
|
|
vp->v_cost = i + n; /* Bytes + blanks. */ |
899 |
|
|
for (n = 0; i != 0; --i, --s) |
900 |
|
|
n = (n << 5) + n + *s; |
901 |
|
|
vp->v_hash = n; /* Hash code. */ |
902 |
|
|
vp->v_flag &= ~VFHBAD; /* Flag as all done. */ |
903 |
|
|
} |
904 |
|
|
} |
905 |
|
|
|
906 |
|
|
/* |
907 |
|
|
* Compute the Insert-Delete |
908 |
|
|
* cost matrix. The dynamic programming algorithm |
909 |
|
|
* described by James Gosling is used. This code assumes |
910 |
|
|
* that the line above the echo line is the last line involved |
911 |
|
|
* in the scroll region. This is easy to arrange on the VT100 |
912 |
|
|
* because of the scrolling region. The "offs" is the origin 0 |
913 |
|
|
* offset of the first row in the virtual/physical screen that |
914 |
|
|
* is being updated; the "size" is the length of the chunk of |
915 |
|
|
* screen being updated. For a full screen update, use offs=0 |
916 |
|
|
* and size=nrow-1. |
917 |
|
|
* |
918 |
|
|
* Older versions of this code implemented the score matrix by |
919 |
|
|
* a two dimensional array of SCORE nodes. This put all kinds of |
920 |
|
|
* multiply instructions in the code! This version is written to |
921 |
|
|
* use a linear array and pointers, and contains no multiplication |
922 |
|
|
* at all. The code has been carefully looked at on the VAX, with |
923 |
|
|
* only marginal checking on other machines for efficiency. In |
924 |
|
|
* fact, this has been tuned twice! Bob McNamara tuned it even |
925 |
|
|
* more for the VAX, which is a big issue for him because of |
926 |
|
|
* the 66 line X displays. |
927 |
|
|
* |
928 |
|
|
* On some machines, replacing the "for (i=1; i<=size; ++i)" with |
929 |
|
|
* i = 1; do { } while (++i <=size)" will make the code quite a |
930 |
|
|
* bit better; but it looks ugly. |
931 |
|
|
*/ |
932 |
|
|
void |
933 |
|
|
setscores(int offs, int size) |
934 |
|
|
{ |
935 |
|
|
struct score *sp; |
936 |
|
|
struct score *sp1; |
937 |
|
|
struct video **vp, **pp; |
938 |
|
|
struct video **vbase, **pbase; |
939 |
|
|
int tempcost; |
940 |
|
|
int bestcost; |
941 |
|
|
int j, i; |
942 |
|
|
|
943 |
|
|
vbase = &vscreen[offs - 1]; /* By hand CSE's. */ |
944 |
|
|
pbase = &pscreen[offs - 1]; |
945 |
|
|
score[0].s_itrace = 0; /* [0, 0] */ |
946 |
|
|
score[0].s_jtrace = 0; |
947 |
|
|
score[0].s_cost = 0; |
948 |
|
|
sp = &score[1]; /* Row 0, inserts. */ |
949 |
|
|
tempcost = 0; |
950 |
|
|
vp = &vbase[1]; |
951 |
|
|
for (j = 1; j <= size; ++j) { |
952 |
|
|
sp->s_itrace = 0; |
953 |
|
|
sp->s_jtrace = j - 1; |
954 |
|
|
tempcost += tcinsl; |
955 |
|
|
tempcost += (*vp)->v_cost; |
956 |
|
|
sp->s_cost = tempcost; |
957 |
|
|
++vp; |
958 |
|
|
++sp; |
959 |
|
|
} |
960 |
|
|
sp = &score[nrow]; /* Column 0, deletes. */ |
961 |
|
|
tempcost = 0; |
962 |
|
|
for (i = 1; i <= size; ++i) { |
963 |
|
|
sp->s_itrace = i - 1; |
964 |
|
|
sp->s_jtrace = 0; |
965 |
|
|
tempcost += tcdell; |
966 |
|
|
sp->s_cost = tempcost; |
967 |
|
|
sp += nrow; |
968 |
|
|
} |
969 |
|
|
sp1 = &score[nrow + 1]; /* [1, 1]. */ |
970 |
|
|
pp = &pbase[1]; |
971 |
|
|
for (i = 1; i <= size; ++i) { |
972 |
|
|
sp = sp1; |
973 |
|
|
vp = &vbase[1]; |
974 |
|
|
for (j = 1; j <= size; ++j) { |
975 |
|
|
sp->s_itrace = i - 1; |
976 |
|
|
sp->s_jtrace = j; |
977 |
|
|
bestcost = (sp - nrow)->s_cost; |
978 |
|
|
if (j != size) /* Cd(A[i])=0 @ Dis. */ |
979 |
|
|
bestcost += tcdell; |
980 |
|
|
tempcost = (sp - 1)->s_cost; |
981 |
|
|
tempcost += (*vp)->v_cost; |
982 |
|
|
if (i != size) /* Ci(B[j])=0 @ Dsj. */ |
983 |
|
|
tempcost += tcinsl; |
984 |
|
|
if (tempcost < bestcost) { |
985 |
|
|
sp->s_itrace = i; |
986 |
|
|
sp->s_jtrace = j - 1; |
987 |
|
|
bestcost = tempcost; |
988 |
|
|
} |
989 |
|
|
tempcost = (sp - nrow - 1)->s_cost; |
990 |
|
|
if ((*pp)->v_color != (*vp)->v_color |
991 |
|
|
|| (*pp)->v_hash != (*vp)->v_hash) |
992 |
|
|
tempcost += (*vp)->v_cost; |
993 |
|
|
if (tempcost < bestcost) { |
994 |
|
|
sp->s_itrace = i - 1; |
995 |
|
|
sp->s_jtrace = j - 1; |
996 |
|
|
bestcost = tempcost; |
997 |
|
|
} |
998 |
|
|
sp->s_cost = bestcost; |
999 |
|
|
++sp; /* Next column. */ |
1000 |
|
|
++vp; |
1001 |
|
|
} |
1002 |
|
|
++pp; |
1003 |
|
|
sp1 += nrow; /* Next row. */ |
1004 |
|
|
} |
1005 |
|
|
} |
1006 |
|
|
|
1007 |
|
|
/* |
1008 |
|
|
* Trace back through the dynamic programming cost |
1009 |
|
|
* matrix, and update the screen using an optimal sequence |
1010 |
|
|
* of redraws, insert lines, and delete lines. The "offs" is |
1011 |
|
|
* the origin 0 offset of the chunk of the screen we are about to |
1012 |
|
|
* update. The "i" and "j" are always started in the lower right |
1013 |
|
|
* corner of the matrix, and imply the size of the screen. |
1014 |
|
|
* A full screen traceback is called with offs=0 and i=j=nrow-1. |
1015 |
|
|
* There is some do-it-yourself double subscripting here, |
1016 |
|
|
* which is acceptable because this routine is much less compute |
1017 |
|
|
* intensive then the code that builds the score matrix! |
1018 |
|
|
*/ |
1019 |
|
|
void |
1020 |
|
|
traceback(int offs, int size, int i, int j) |
1021 |
|
|
{ |
1022 |
|
|
int itrace, jtrace; |
1023 |
|
|
int k; |
1024 |
|
|
int ninsl, ndraw, ndell; |
1025 |
|
|
|
1026 |
|
|
if (i == 0 && j == 0) /* End of update. */ |
1027 |
|
|
return; |
1028 |
|
|
itrace = score[(nrow * i) + j].s_itrace; |
1029 |
|
|
jtrace = score[(nrow * i) + j].s_jtrace; |
1030 |
|
|
if (itrace == i) { /* [i, j-1] */ |
1031 |
|
|
ninsl = 0; /* Collect inserts. */ |
1032 |
|
|
if (i != size) |
1033 |
|
|
ninsl = 1; |
1034 |
|
|
ndraw = 1; |
1035 |
|
|
while (itrace != 0 || jtrace != 0) { |
1036 |
|
|
if (score[(nrow * itrace) + jtrace].s_itrace != itrace) |
1037 |
|
|
break; |
1038 |
|
|
jtrace = score[(nrow * itrace) + jtrace].s_jtrace; |
1039 |
|
|
if (i != size) |
1040 |
|
|
++ninsl; |
1041 |
|
|
++ndraw; |
1042 |
|
|
} |
1043 |
|
|
traceback(offs, size, itrace, jtrace); |
1044 |
|
|
if (ninsl != 0) { |
1045 |
|
|
ttcolor(CTEXT); |
1046 |
|
|
ttinsl(offs + j - ninsl, offs + size - 1, ninsl); |
1047 |
|
|
} |
1048 |
|
|
do { /* B[j], A[j] blank. */ |
1049 |
|
|
k = offs + j - ndraw; |
1050 |
|
|
uline(k, vscreen[k], &blanks); |
1051 |
|
|
} while (--ndraw); |
1052 |
|
|
return; |
1053 |
|
|
} |
1054 |
|
|
if (jtrace == j) { /* [i-1, j] */ |
1055 |
|
|
ndell = 0; /* Collect deletes. */ |
1056 |
|
|
if (j != size) |
1057 |
|
|
ndell = 1; |
1058 |
|
|
while (itrace != 0 || jtrace != 0) { |
1059 |
|
|
if (score[(nrow * itrace) + jtrace].s_jtrace != jtrace) |
1060 |
|
|
break; |
1061 |
|
|
itrace = score[(nrow * itrace) + jtrace].s_itrace; |
1062 |
|
|
if (j != size) |
1063 |
|
|
++ndell; |
1064 |
|
|
} |
1065 |
|
|
if (ndell != 0) { |
1066 |
|
|
ttcolor(CTEXT); |
1067 |
|
|
ttdell(offs + i - ndell, offs + size - 1, ndell); |
1068 |
|
|
} |
1069 |
|
|
traceback(offs, size, itrace, jtrace); |
1070 |
|
|
return; |
1071 |
|
|
} |
1072 |
|
|
traceback(offs, size, itrace, jtrace); |
1073 |
|
|
k = offs + j - 1; |
1074 |
|
|
uline(k, vscreen[k], pscreen[offs + i - 1]); |
1075 |
|
|
} |