1 /*
2 * ed.refresh.c: Lower level screen refreshing functions
3 */
4 /*-
5 * Copyright (c) 1980, 1991 The Regents of the University of California.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32 #include "sh.h"
33 #include "ed.h"
34 /* #define DEBUG_UPDATE */
35 /* #define DEBUG_REFRESH */
36 /* #define DEBUG_LITERAL */
37
38 /* refresh.c -- refresh the current set of lines on the screen */
39
40 Char *litptr;
41 static int vcursor_h, vcursor_v;
42 static int rprompt_h, rprompt_v;
43
44 static int MakeLiteral (Char *, int, Char);
45 static int Draw (Char *, int, int);
46 static void Vdraw (Char, int);
47 static void RefreshPromptpart (Char *);
48 static void update_line (Char *, Char *, int);
49 static void str_insert (Char *, int, int, Char *, int);
50 static void str_delete (Char *, int, int, int);
51 static void str_cp (Char *, Char *, int);
52 #ifndef WINNT_NATIVE
53 static
54 #else
55 extern
56 #endif
57 void PutPlusOne (Char, int);
58 static void cpy_pad_spaces (Char *, Char *, int);
59 #if defined(DEBUG_UPDATE) || defined(DEBUG_REFRESH) || defined(DEBUG_LITERAL)
60 static void reprintf (char *, ...);
61 #ifdef DEBUG_UPDATE
62 static void dprintstr (char *, const Char *, const Char *);
63
64 static void
dprintstr(char * str,const Char * f,const Char * t)65 dprintstr(char *str, const Char *f, const Char *t)
66 {
67 reprintf("%s:\"", str);
68 while (f < t) {
69 if (ASC(*f) & ~ASCII)
70 reprintf("[%x]", *f++);
71 else
72 reprintf("%c", CTL_ESC(ASCII & ASC(*f++)));
73 }
74 reprintf("\"\r\n");
75 }
76 #endif /* DEBUG_UPDATE */
77
78 /* reprintf():
79 * Print to $DEBUGTTY, so that we can test editing on one pty, and
80 * print debugging stuff on another. Don't interrupt the shell while
81 * debugging cause you'll mangle up the file descriptors!
82 */
83 static void
reprintf(char * fmt,...)84 reprintf(char *fmt, ...)
85 {
86 static int fd = -1;
87 char *dtty;
88
89 if ((dtty = getenv("DEBUGTTY"))) {
90 int o;
91 va_list va;
92 va_start(va, fmt);
93
94 if (fd == -1)
95 fd = xopen(dtty, O_RDWR);
96 o = SHOUT;
97 flush();
98 SHOUT = fd;
99 xvprintf(fmt, va);
100 va_end(va);
101 flush();
102 SHOUT = o;
103 }
104 }
105 #endif /* DEBUG_UPDATE || DEBUG_REFRESH || DEBUG_LITERAL */
106
107 static int litlen = 0, litalloc = 0;
108
MakeLiteral(Char * str,int len,Char addlit)109 static int MakeLiteral(Char *str, int len, Char addlit)
110 {
111 int i, addlitlen = 0;
112 Char *addlitptr = 0;
113 if (addlit) {
114 if ((addlit & LITERAL) != 0) {
115 addlitptr = litptr + (addlit & ~LITERAL) * LIT_FACTOR;
116 addlitlen = Strlen(addlitptr);
117 } else {
118 addlitptr = &addlit;
119 addlitlen = 1;
120 }
121 for (i = 0; i < litlen; i += LIT_FACTOR)
122 if (!Strncmp(addlitptr, litptr + i, addlitlen) && !Strncmp(str, litptr + i + addlitlen, len) && litptr[i + addlitlen + len] == 0)
123 return (i / LIT_FACTOR) | LITERAL;
124 } else {
125 addlitlen = 0;
126 for (i = 0; i < litlen; i += LIT_FACTOR)
127 if (!Strncmp(str, litptr + i, len) && litptr[i + len] == 0)
128 return (i / LIT_FACTOR) | LITERAL;
129 }
130 if (litlen + addlitlen + len + 1 + (LIT_FACTOR - 1) > litalloc) {
131 Char *newlitptr;
132 int add = 256;
133 while (len + addlitlen + 1 + (LIT_FACTOR - 1) > add)
134 add *= 2;
135 newlitptr = xrealloc(litptr, (litalloc + add) * sizeof(Char));
136 if (!newlitptr)
137 return '?';
138 litptr = newlitptr;
139 litalloc += add;
140 if (addlitptr && addlitptr != &addlit)
141 addlitptr = litptr + (addlit & ~LITERAL) * LIT_FACTOR;
142 }
143 i = litlen / LIT_FACTOR;
144 if (i >= LITERAL || i == CHAR_DBWIDTH)
145 return '?';
146 if (addlitptr) {
147 Strncpy(litptr + litlen, addlitptr, addlitlen);
148 litlen += addlitlen;
149 }
150 Strncpy(litptr + litlen, str, len);
151 litlen += len;
152 do
153 litptr[litlen++] = 0;
154 while (litlen % LIT_FACTOR);
155 return i | LITERAL;
156 }
157
158 /* draw char at cp, expand tabs, ctl chars */
159 static int
Draw(Char * cp,int nocomb,int drawPrompt)160 Draw(Char *cp, int nocomb, int drawPrompt)
161 {
162 int w, i, lv, lh;
163 Char c, attr;
164
165 #ifdef WIDE_STRINGS
166 if (!drawPrompt) { /* draw command-line */
167 attr = 0;
168 c = *cp;
169 } else { /* draw prompt */
170 /* prompt with attributes(UNDER,BOLD,STANDOUT) */
171 if (*cp & (UNDER | BOLD | STANDOUT)) { /* *cp >= STANDOUT */
172
173 /* example)
174 * We can't distinguish whether (*cp=)0x02ffffff is
175 * U+02FFFFFF or U+00FFFFFF|STANDOUT.
176 * We handle as U+00FFFFFF|STANDOUT, only when drawing prompt. */
177 attr = (*cp & ATTRIBUTES);
178 /* ~(UNDER | BOLD | STANDOUT) = 0xf1ffffff */
179 c = *cp & ~(UNDER | BOLD | STANDOUT);
180
181 /* if c is ctrl code, we handle *cp as havnig no attributes */
182 if ((c < 0x20 && c >= 0) || c == 0x7f) {
183 attr = 0;
184 c = *cp;
185 }
186 } else { /* prompt without attributes */
187 attr = 0;
188 c = *cp;
189 }
190 }
191 #else
192 attr = *cp & ~CHAR;
193 c = *cp & CHAR;
194 #endif
195 w = NLSClassify(c, nocomb, drawPrompt);
196 switch (w) {
197 case NLSCLASS_NL:
198 Vdraw('\0', 0); /* assure end of line */
199 vcursor_h = 0; /* reset cursor pos */
200 vcursor_v++;
201 break;
202 case NLSCLASS_TAB:
203 do {
204 Vdraw(' ', 1);
205 } while ((vcursor_h & 07) != 0);
206 break;
207 case NLSCLASS_CTRL:
208 Vdraw('^' | attr, 1);
209 if (c == CTL_ESC('\177')) {
210 Vdraw('?' | attr, 1);
211 } else {
212 #ifdef IS_ASCII
213 /* uncontrolify it; works only for iso8859-1 like sets */
214 Vdraw(c | 0100 | attr, 1);
215 #else
216 Vdraw(_toebcdic[_toascii[c]|0100] | attr, 1);
217 #endif
218 }
219 break;
220 case NLSCLASS_ILLEGAL:
221 Vdraw('\\' | attr, 1);
222 Vdraw((((c >> 6) & 7) + '0') | attr, 1);
223 Vdraw((((c >> 3) & 7) + '0') | attr, 1);
224 Vdraw(((c & 7) + '0') | attr, 1);
225 break;
226 case NLSCLASS_ILLEGAL2:
227 case NLSCLASS_ILLEGAL3:
228 case NLSCLASS_ILLEGAL4:
229 case NLSCLASS_ILLEGAL5:
230 Vdraw('\\', 1);
231 Vdraw('U', 1);
232 Vdraw('+', 1);
233 for (i = 16 + 4 * (-w-5); i >= 0; i -= 4)
234 Vdraw("0123456789ABCDEF"[(c >> i) & 15] | attr, 1);
235 break;
236 case 0:
237 lv = vcursor_v;
238 lh = vcursor_h;
239 for (;;) {
240 lh--;
241 if (lh < 0) {
242 lv--;
243 if (lv < 0)
244 break;
245 lh = Strlen(Vdisplay[lv]) - 1;
246 }
247 if (Vdisplay[lv][lh] != CHAR_DBWIDTH)
248 break;
249 }
250 if (lv < 0) {
251 Vdraw('\\' | attr, 1);
252 Vdraw((((c >> 6) & 7) + '0') | attr, 1);
253 Vdraw((((c >> 3) & 7) + '0') | attr, 1);
254 Vdraw(((c & 7) + '0') | attr, 1);
255 break;
256 }
257 Vdisplay[lv][lh] = MakeLiteral(cp, 1, Vdisplay[lv][lh]);
258 break;
259 default:
260 Vdraw(*cp, w);
261 break;
262 }
263 return 1;
264 }
265
266 static void
Vdraw(Char c,int width)267 Vdraw(Char c, int width) /* draw char c onto V lines */
268 {
269 #ifdef DEBUG_REFRESH
270 # ifdef SHORT_STRINGS
271 reprintf("Vdrawing %6.6o '%c' %d\r\n", (unsigned)c, (int)(c & ASCII), width);
272 # else
273 reprintf("Vdrawing %3.3o '%c' %d\r\n", (unsigned)c, (int)c, width);
274 # endif /* SHORT_STRNGS */
275 #endif /* DEBUG_REFRESH */
276
277 /* Hopefully this is what all the terminals do with multi-column characters
278 that "span line breaks". */
279 while (vcursor_h + width > TermH)
280 Vdraw(' ', 1);
281 Vdisplay[vcursor_v][vcursor_h] = c;
282 if (width)
283 vcursor_h++; /* advance to next place */
284 while (--width > 0)
285 Vdisplay[vcursor_v][vcursor_h++] = CHAR_DBWIDTH;
286 if (vcursor_h >= TermH) {
287 Vdisplay[vcursor_v][TermH] = '\0'; /* assure end of line */
288 vcursor_h = 0; /* reset it. */
289 vcursor_v++;
290 #ifdef DEBUG_REFRESH
291 if (vcursor_v >= TermV) { /* should NEVER happen. */
292 reprintf("\r\nVdraw: vcursor_v overflow! Vcursor_v == %d > %d\r\n",
293 vcursor_v, TermV);
294 abort();
295 }
296 #endif /* DEBUG_REFRESH */
297 }
298 }
299
300 /*
301 * RefreshPromptpart()
302 * draws a prompt element, expanding literals (we know it's ASCIZ)
303 */
304 static void
RefreshPromptpart(Char * buf)305 RefreshPromptpart(Char *buf)
306 {
307 Char *cp;
308 int w;
309
310 if (buf == NULL)
311 return;
312 for (cp = buf; *cp; ) {
313 if (*cp & LITERAL) {
314 Char *litstart = cp;
315 while (*cp & LITERAL)
316 cp++;
317 if (*cp) {
318 w = NLSWidth(*cp & CHAR);
319 Vdraw(MakeLiteral(litstart, cp + 1 - litstart, 0), w);
320 cp++;
321 }
322 else {
323 /*
324 * XXX: This is a bug, we lose the last literal, if it is not
325 * followed by a normal character, but it is too hard to fix
326 */
327 break;
328 }
329 }
330 else
331 cp += Draw(cp, cp == buf, 1);
332 }
333 }
334
335 /*
336 * Refresh()
337 * draws the new virtual screen image from the current input
338 * line, then goes line-by-line changing the real image to the new
339 * virtual image. The routine to re-draw a line can be replaced
340 * easily in hopes of a smarter one being placed there.
341 */
342 #ifndef WINNT_NATIVE
343 static
344 #endif
345 int OldvcV = 0;
346
347 void
Refresh(void)348 Refresh(void)
349 {
350 int cur_line;
351 Char *cp;
352 int cur_h, cur_v = 0, new_vcv;
353 int rhdiff;
354 Char oldgetting;
355
356 #ifdef DEBUG_REFRESH
357 reprintf("Prompt = :%s:\r\n", short2str(Prompt));
358 reprintf("InputBuf = :%s:\r\n", short2str(InputBuf));
359 #endif /* DEBUG_REFRESH */
360 oldgetting = GettingInput;
361 GettingInput = 0; /* avoid re-entrance via SIGWINCH */
362
363 /* reset the Vdraw cursor, temporarily draw rprompt to calculate its size */
364 vcursor_h = 0;
365 vcursor_v = 0;
366 RefreshPromptpart(RPrompt);
367 rprompt_h = vcursor_h;
368 rprompt_v = vcursor_v;
369
370 /* reset the Vdraw cursor, draw prompt */
371 vcursor_h = 0;
372 vcursor_v = 0;
373 RefreshPromptpart(Prompt);
374 cur_h = -1; /* set flag in case I'm not set */
375
376 /* draw the current input buffer */
377 for (cp = InputBuf; (cp < LastChar); ) {
378 if (cp >= Cursor && cur_h == -1) {
379 cur_h = vcursor_h; /* save for later */
380 cur_v = vcursor_v;
381 Cursor = cp;
382 }
383 cp += Draw(cp, cp == InputBuf, 0);
384 }
385
386 if (cur_h == -1) { /* if I haven't been set yet, I'm at the end */
387 cur_h = vcursor_h;
388 cur_v = vcursor_v;
389 }
390
391 rhdiff = TermH - vcursor_h - rprompt_h;
392 if (rprompt_h != 0 && rprompt_v == 0 && vcursor_v == 0 && rhdiff > 1) {
393 /*
394 * have a right-hand side prompt that will fit on
395 * the end of the first line with at least one
396 * character gap to the input buffer.
397 */
398 while (--rhdiff > 0) /* pad out with spaces */
399 Vdraw(' ', 1);
400 RefreshPromptpart(RPrompt);
401 }
402 else {
403 rprompt_h = 0; /* flag "not using rprompt" */
404 rprompt_v = 0;
405 }
406
407 new_vcv = vcursor_v; /* must be done BEFORE the NUL is written */
408 Vdraw('\0', 1); /* put NUL on end */
409
410 #if defined (DEBUG_REFRESH)
411 reprintf("TermH=%d, vcur_h=%d, vcur_v=%d, Vdisplay[0]=\r\n:%80.80s:\r\n",
412 TermH, vcursor_h, vcursor_v, short2str(Vdisplay[0]));
413 #endif /* DEBUG_REFRESH */
414
415 #ifdef DEBUG_UPDATE
416 reprintf("updating %d lines.\r\n", new_vcv);
417 #endif /* DEBUG_UPDATE */
418 for (cur_line = 0; cur_line <= new_vcv; cur_line++) {
419 /* NOTE THAT update_line MAY CHANGE Display[cur_line] */
420 update_line(Display[cur_line], Vdisplay[cur_line], cur_line);
421 #ifdef WINNT_NATIVE
422 flush();
423 #endif /* WINNT_NATIVE */
424
425 /*
426 * Copy the new line to be the current one, and pad out with spaces
427 * to the full width of the terminal so that if we try moving the
428 * cursor by writing the character that is at the end of the
429 * screen line, it won't be a NUL or some old leftover stuff.
430 */
431 cpy_pad_spaces(Display[cur_line], Vdisplay[cur_line], TermH);
432 }
433 #ifdef DEBUG_REFRESH
434 reprintf("\r\nvcursor_v = %d, OldvcV = %d, cur_line = %d\r\n",
435 vcursor_v, OldvcV, cur_line);
436 #endif /* DEBUG_REFRESH */
437 if (OldvcV > new_vcv) {
438 for (; cur_line <= OldvcV; cur_line++) {
439 update_line(Display[cur_line], STRNULL, cur_line);
440 *Display[cur_line] = '\0';
441 }
442 }
443 OldvcV = new_vcv; /* set for next time */
444 #ifdef DEBUG_REFRESH
445 reprintf("\r\nCursorH = %d, CursorV = %d, cur_h = %d, cur_v = %d\r\n",
446 CursorH, CursorV, cur_h, cur_v);
447 #endif /* DEBUG_REFRESH */
448 #ifdef WINNT_NATIVE
449 flush();
450 #endif /* WINNT_NATIVE */
451 MoveToLine(cur_v); /* go to where the cursor is */
452 MoveToChar(cur_h);
453 SetAttributes(0); /* Clear all attributes */
454 flush(); /* send the output... */
455 GettingInput = oldgetting; /* reset to old value */
456 }
457
458 #ifdef notdef
GotoBottom(void)459 GotoBottom(void)
460 { /* used to go to last used screen line */
461 MoveToLine(OldvcV);
462 }
463
464 #endif
465
466 void
PastBottom(void)467 PastBottom(void)
468 { /* used to go to last used screen line */
469 MoveToLine(OldvcV);
470 (void) putraw('\r');
471 (void) putraw('\n');
472 ClearDisp();
473 flush();
474 }
475
476
477 /* insert num characters of s into d (in front of the character) at dat,
478 maximum length of d is dlen */
479 static void
str_insert(Char * d,int dat,int dlen,Char * s,int num)480 str_insert(Char *d, int dat, int dlen, Char *s, int num)
481 {
482 Char *a, *b;
483
484 if (num <= 0)
485 return;
486 if (num > dlen - dat)
487 num = dlen - dat;
488
489 #ifdef DEBUG_REFRESH
490 reprintf("str_insert() starting: %d at %d max %d, d == \"%s\"\n",
491 num, dat, dlen, short2str(d));
492 reprintf("s == \"%s\"n", short2str(s));
493 #endif /* DEBUG_REFRESH */
494
495 /* open up the space for num chars */
496 if (num > 0) {
497 b = d + dlen - 1;
498 a = b - num;
499 while (a >= &d[dat])
500 *b-- = *a--;
501 d[dlen] = '\0'; /* just in case */
502 }
503 #ifdef DEBUG_REFRESH
504 reprintf("str_insert() after insert: %d at %d max %d, d == \"%s\"\n",
505 num, dat, dlen, short2str(d));
506 reprintf("s == \"%s\"n", short2str(s));
507 #endif /* DEBUG_REFRESH */
508
509 /* copy the characters */
510 for (a = d + dat; (a < d + dlen) && (num > 0); num--)
511 *a++ = *s++;
512
513 #ifdef DEBUG_REFRESH
514 reprintf("str_insert() after copy: %d at %d max %d, d == \"%s\"\n",
515 num, dat, dlen, d, short2str(s));
516 reprintf("s == \"%s\"n", short2str(s));
517 #endif /* DEBUG_REFRESH */
518 }
519
520 /* delete num characters d at dat, maximum length of d is dlen */
521 static void
str_delete(Char * d,int dat,int dlen,int num)522 str_delete(Char *d, int dat, int dlen, int num)
523 {
524 Char *a, *b;
525
526 if (num <= 0)
527 return;
528 if (dat + num >= dlen) {
529 d[dat] = '\0';
530 return;
531 }
532
533 #ifdef DEBUG_REFRESH
534 reprintf("str_delete() starting: %d at %d max %d, d == \"%s\"\n",
535 num, dat, dlen, short2str(d));
536 #endif /* DEBUG_REFRESH */
537
538 /* open up the space for num chars */
539 if (num > 0) {
540 b = d + dat;
541 a = b + num;
542 while (a < &d[dlen])
543 *b++ = *a++;
544 d[dlen] = '\0'; /* just in case */
545 }
546 #ifdef DEBUG_REFRESH
547 reprintf("str_delete() after delete: %d at %d max %d, d == \"%s\"\n",
548 num, dat, dlen, short2str(d));
549 #endif /* DEBUG_REFRESH */
550 }
551
552 static void
str_cp(Char * a,Char * b,int n)553 str_cp(Char *a, Char *b, int n)
554 {
555 while (n-- && *b)
556 *a++ = *b++;
557 }
558
559
560 /* ****************************************************************
561 update_line() is based on finding the middle difference of each line
562 on the screen; vis:
563
564 /old first difference
565 /beginning of line | /old last same /old EOL
566 v v v v
567 old: eddie> Oh, my little gruntle-buggy is to me, as lurgid as
568 new: eddie> Oh, my little buggy says to me, as lurgid as
569 ^ ^ ^ ^
570 \beginning of line | \new last same \new end of line
571 \new first difference
572
573 all are character pointers for the sake of speed. Special cases for
574 no differences, as well as for end of line additions must be handled.
575 **************************************************************** */
576
577 /* Minimum at which doing an insert it "worth it". This should be about
578 * half the "cost" of going into insert mode, inserting a character, and
579 * going back out. This should really be calculated from the termcap
580 * data... For the moment, a good number for ANSI terminals.
581 */
582 #define MIN_END_KEEP 4
583
584 static void /* could be changed to make it smarter */
update_line(Char * old,Char * new,int cur_line)585 update_line(Char *old, Char *new, int cur_line)
586 {
587 Char *o, *n, *p, c;
588 Char *ofd, *ols, *oe, *nfd, *nls, *ne;
589 Char *osb, *ose, *nsb, *nse;
590 int fx, sx;
591
592 /*
593 * find first diff (won't be CHAR_DBWIDTH in either line)
594 */
595 for (o = old, n = new; *o && (*o == *n); o++, n++)
596 continue;
597 ofd = o;
598 nfd = n;
599
600 /*
601 * Find the end of both old and new
602 */
603 o = Strend(o);
604
605 /*
606 * Remove any trailing blanks off of the end, being careful not to
607 * back up past the beginning.
608 */
609 if (!(adrof(STRhighlight) && MarkIsSet)) {
610 while (ofd < o) {
611 if (o[-1] != ' ')
612 break;
613 o--;
614 }
615 }
616 oe = o;
617 *oe = (Char) 0;
618
619 n = Strend(n);
620
621 /* remove blanks from end of new */
622 if (!(adrof(STRhighlight) && MarkIsSet)) {
623 while (nfd < n) {
624 if (n[-1] != ' ')
625 break;
626 n--;
627 }
628 }
629 ne = n;
630 *ne = (Char) 0;
631
632 /*
633 * if no diff, continue to next line of redraw
634 */
635 if (*ofd == '\0' && *nfd == '\0') {
636 #ifdef DEBUG_UPDATE
637 reprintf("no difference.\r\n");
638 #endif /* DEBUG_UPDATE */
639 return;
640 }
641
642 /*
643 * find last same pointer
644 */
645 while ((o > ofd) && (n > nfd) && (*--o == *--n))
646 continue;
647 if (*o != *n) {
648 o++;
649 n++;
650 }
651 while (*o == CHAR_DBWIDTH) {
652 o++;
653 n++;
654 }
655 ols = o;
656 nls = n;
657
658 /*
659 * find same begining and same end
660 */
661 osb = ols;
662 nsb = nls;
663 ose = ols;
664 nse = nls;
665
666 /*
667 * case 1: insert: scan from nfd to nls looking for *ofd
668 */
669 if (*ofd) {
670 for (c = *ofd, n = nfd; n < nls; n++) {
671 if (c == *n) {
672 for (o = ofd, p = n; p < nls && o < ols && *o == *p; o++, p++)
673 continue;
674 /*
675 * if the new match is longer and it's worth keeping, then we
676 * take it
677 */
678 if (((nse - nsb) < (p - n)) && (2 * (p - n) > n - nfd)) {
679 nsb = n;
680 nse = p;
681 osb = ofd;
682 ose = o;
683 }
684 }
685 }
686 }
687
688 /*
689 * case 2: delete: scan from ofd to ols looking for *nfd
690 */
691 if (*nfd) {
692 for (c = *nfd, o = ofd; o < ols; o++) {
693 if (c == *o) {
694 for (n = nfd, p = o; p < ols && n < nls && *p == *n; p++, n++)
695 continue;
696 /*
697 * if the new match is longer and it's worth keeping, then we
698 * take it
699 */
700 if (((ose - osb) < (p - o)) && (2 * (p - o) > o - ofd)) {
701 nsb = nfd;
702 nse = n;
703 osb = o;
704 ose = p;
705 }
706 }
707 }
708 }
709 #ifdef notdef
710 /*
711 * If `last same' is before `same end' re-adjust
712 */
713 if (ols < ose)
714 ols = ose;
715 if (nls < nse)
716 nls = nse;
717 #endif
718
719 /*
720 * Pragmatics I: If old trailing whitespace or not enough characters to
721 * save to be worth it, then don't save the last same info.
722 */
723 if ((oe - ols) < MIN_END_KEEP) {
724 ols = oe;
725 nls = ne;
726 }
727
728 /*
729 * Pragmatics II: if the terminal isn't smart enough, make the data dumber
730 * so the smart update doesn't try anything fancy
731 */
732
733 /*
734 * fx is the number of characters we need to insert/delete: in the
735 * beginning to bring the two same begins together
736 */
737 fx = (int) ((nsb - nfd) - (osb - ofd));
738 /*
739 * sx is the number of characters we need to insert/delete: in the end to
740 * bring the two same last parts together
741 */
742 sx = (int) ((nls - nse) - (ols - ose));
743
744 if (!T_CanIns) {
745 if (fx > 0) {
746 osb = ols;
747 ose = ols;
748 nsb = nls;
749 nse = nls;
750 }
751 if (sx > 0) {
752 ols = oe;
753 nls = ne;
754 }
755 if ((ols - ofd) < (nls - nfd)) {
756 ols = oe;
757 nls = ne;
758 }
759 }
760 if (!T_CanDel) {
761 if (fx < 0) {
762 osb = ols;
763 ose = ols;
764 nsb = nls;
765 nse = nls;
766 }
767 if (sx < 0) {
768 ols = oe;
769 nls = ne;
770 }
771 if ((ols - ofd) > (nls - nfd)) {
772 ols = oe;
773 nls = ne;
774 }
775 }
776
777 /*
778 * Pragmatics III: make sure the middle shifted pointers are correct if
779 * they don't point to anything (we may have moved ols or nls).
780 */
781 /* if the change isn't worth it, don't bother */
782 /* was: if (osb == ose) */
783 if ((ose - osb) < MIN_END_KEEP) {
784 osb = ols;
785 ose = ols;
786 nsb = nls;
787 nse = nls;
788 }
789
790 /*
791 * Now that we are done with pragmatics we recompute fx, sx
792 */
793 fx = (int) ((nsb - nfd) - (osb - ofd));
794 sx = (int) ((nls - nse) - (ols - ose));
795
796 #ifdef DEBUG_UPDATE
797 reprintf("\n");
798 reprintf("ofd %d, osb %d, ose %d, ols %d, oe %d\n",
799 ofd - old, osb - old, ose - old, ols - old, oe - old);
800 reprintf("nfd %d, nsb %d, nse %d, nls %d, ne %d\n",
801 nfd - new, nsb - new, nse - new, nls - new, ne - new);
802 reprintf("xxx-xxx:\"00000000001111111111222222222233333333334\"\r\n");
803 reprintf("xxx-xxx:\"01234567890123456789012345678901234567890\"\r\n");
804 dprintstr("old- oe", old, oe);
805 dprintstr("new- ne", new, ne);
806 dprintstr("old-ofd", old, ofd);
807 dprintstr("new-nfd", new, nfd);
808 dprintstr("ofd-osb", ofd, osb);
809 dprintstr("nfd-nsb", nfd, nsb);
810 dprintstr("osb-ose", osb, ose);
811 dprintstr("nsb-nse", nsb, nse);
812 dprintstr("ose-ols", ose, ols);
813 dprintstr("nse-nls", nse, nls);
814 dprintstr("ols- oe", ols, oe);
815 dprintstr("nls- ne", nls, ne);
816 #endif /* DEBUG_UPDATE */
817
818 /*
819 * CursorV to this line cur_line MUST be in this routine so that if we
820 * don't have to change the line, we don't move to it. CursorH to first
821 * diff char
822 */
823 MoveToLine(cur_line);
824
825 /*
826 * at this point we have something like this:
827 *
828 * /old /ofd /osb /ose /ols /oe
829 * v.....................v v..................v v........v
830 * eddie> Oh, my fredded gruntle-buggy is to me, as foo var lurgid as
831 * eddie> Oh, my fredded quiux buggy is to me, as gruntle-lurgid as
832 * ^.....................^ ^..................^ ^........^
833 * \new \nfd \nsb \nse \nls \ne
834 *
835 * fx is the difference in length between the the chars between nfd and
836 * nsb, and the chars between ofd and osb, and is thus the number of
837 * characters to delete if < 0 (new is shorter than old, as above),
838 * or insert (new is longer than short).
839 *
840 * sx is the same for the second differences.
841 */
842
843 /*
844 * if we have a net insert on the first difference, AND inserting the net
845 * amount ((nsb-nfd) - (osb-ofd)) won't push the last useful character
846 * (which is ne if nls != ne, otherwise is nse) off the edge of the screen
847 * (TermH - 1) else we do the deletes first so that we keep everything we
848 * need to.
849 */
850
851 /*
852 * if the last same is the same like the end, there is no last same part,
853 * otherwise we want to keep the last same part set p to the last useful
854 * old character
855 */
856 p = (ols != oe) ? oe : ose;
857
858 /*
859 * if (There is a diffence in the beginning) && (we need to insert
860 * characters) && (the number of characters to insert is less than the term
861 * width) We need to do an insert! else if (we need to delete characters)
862 * We need to delete characters! else No insert or delete
863 */
864 if ((nsb != nfd) && fx > 0 && ((p - old) + fx < TermH)) {
865 #ifdef DEBUG_UPDATE
866 reprintf("first diff insert at %d...\r\n", nfd - new);
867 #endif /* DEBUG_UPDATE */
868 /*
869 * Move to the first char to insert, where the first diff is.
870 */
871 MoveToChar(nfd - new);
872 /*
873 * Check if we have stuff to keep at end
874 */
875 if (nsb != ne) {
876 #ifdef DEBUG_UPDATE
877 reprintf("with stuff to keep at end\r\n");
878 #endif /* DEBUG_UPDATE */
879 /*
880 * insert fx chars of new starting at nfd
881 */
882 if (fx > 0) {
883 #ifdef DEBUG_UPDATE
884 if (!T_CanIns)
885 reprintf(" ERROR: cannot insert in early first diff\n");
886 #endif /* DEBUG_UPDATE */
887 Insert_write(nfd, fx);
888 str_insert(old, (int) (ofd - old), TermH, nfd, fx);
889 }
890 /*
891 * write (nsb-nfd) - fx chars of new starting at (nfd + fx)
892 */
893 so_write(nfd + fx, (nsb - nfd) - fx);
894 str_cp(ofd + fx, nfd + fx, (int) ((nsb - nfd) - fx));
895 }
896 else {
897 #ifdef DEBUG_UPDATE
898 reprintf("without anything to save\r\n");
899 #endif /* DEBUG_UPDATE */
900 so_write(nfd, (nsb - nfd));
901 str_cp(ofd, nfd, (int) (nsb - nfd));
902 /*
903 * Done
904 */
905 return;
906 }
907 }
908 else if (fx < 0) {
909 #ifdef DEBUG_UPDATE
910 reprintf("first diff delete at %d...\r\n", ofd - old);
911 #endif /* DEBUG_UPDATE */
912 /*
913 * move to the first char to delete where the first diff is
914 */
915 MoveToChar(ofd - old);
916 /*
917 * Check if we have stuff to save
918 */
919 if (osb != oe) {
920 #ifdef DEBUG_UPDATE
921 reprintf("with stuff to save at end\r\n");
922 #endif /* DEBUG_UPDATE */
923 /*
924 * fx is less than zero *always* here but we check for code
925 * symmetry
926 */
927 if (fx < 0) {
928 #ifdef DEBUG_UPDATE
929 if (!T_CanDel)
930 reprintf(" ERROR: cannot delete in first diff\n");
931 #endif /* DEBUG_UPDATE */
932 DeleteChars(-fx);
933 str_delete(old, (int) (ofd - old), TermH, -fx);
934 }
935 /*
936 * write (nsb-nfd) chars of new starting at nfd
937 */
938 so_write(nfd, (nsb - nfd));
939 str_cp(ofd, nfd, (int) (nsb - nfd));
940
941 }
942 else {
943 #ifdef DEBUG_UPDATE
944 reprintf("but with nothing left to save\r\n");
945 #endif /* DEBUG_UPDATE */
946 /*
947 * write (nsb-nfd) chars of new starting at nfd
948 */
949 so_write(nfd, (nsb - nfd));
950 #ifdef DEBUG_REFRESH
951 reprintf("cleareol %d\n", (oe - old) - (ne - new));
952 #endif /* DEBUG_UPDATE */
953 #ifndef WINNT_NATIVE
954 ClearEOL((oe - old) - (ne - new));
955 #else
956 /*
957 * The calculation above does not work too well on NT
958 */
959 ClearEOL(TermH - CursorH);
960 #endif /*WINNT_NATIVE*/
961 /*
962 * Done
963 */
964 return;
965 }
966 }
967 else
968 fx = 0;
969
970 if (sx < 0) {
971 #ifdef DEBUG_UPDATE
972 reprintf("second diff delete at %d...\r\n", (ose - old) + fx);
973 #endif /* DEBUG_UPDATE */
974 /*
975 * Check if we have stuff to delete
976 */
977 /*
978 * fx is the number of characters inserted (+) or deleted (-)
979 */
980
981 MoveToChar((ose - old) + fx);
982 /*
983 * Check if we have stuff to save
984 */
985 if (ols != oe) {
986 #ifdef DEBUG_UPDATE
987 reprintf("with stuff to save at end\r\n");
988 #endif /* DEBUG_UPDATE */
989 /*
990 * Again a duplicate test.
991 */
992 if (sx < 0) {
993 #ifdef DEBUG_UPDATE
994 if (!T_CanDel)
995 reprintf(" ERROR: cannot delete in second diff\n");
996 #endif /* DEBUG_UPDATE */
997 DeleteChars(-sx);
998 }
999
1000 /*
1001 * write (nls-nse) chars of new starting at nse
1002 */
1003 so_write(nse, (nls - nse));
1004 }
1005 else {
1006 int olen = (int) (oe - old + fx);
1007 if (olen > TermH)
1008 olen = TermH;
1009 #ifdef DEBUG_UPDATE
1010 reprintf("but with nothing left to save\r\n");
1011 #endif /* DEBUG_UPDATE */
1012 so_write(nse, (nls - nse));
1013 #ifdef DEBUG_REFRESH
1014 reprintf("cleareol %d\n", olen - (ne - new));
1015 #endif /* DEBUG_UPDATE */
1016 #ifndef WINNT_NATIVE
1017 ClearEOL(olen - (ne - new));
1018 #else
1019 /*
1020 * The calculation above does not work too well on NT
1021 */
1022 ClearEOL(TermH - CursorH);
1023 #endif /*WINNT_NATIVE*/
1024 }
1025 }
1026
1027 /*
1028 * if we have a first insert AND WE HAVEN'T ALREADY DONE IT...
1029 */
1030 if ((nsb != nfd) && (osb - ofd) <= (nsb - nfd) && (fx == 0)) {
1031 #ifdef DEBUG_UPDATE
1032 reprintf("late first diff insert at %d...\r\n", nfd - new);
1033 #endif /* DEBUG_UPDATE */
1034
1035 MoveToChar(nfd - new);
1036 /*
1037 * Check if we have stuff to keep at the end
1038 */
1039 if (nsb != ne) {
1040 #ifdef DEBUG_UPDATE
1041 reprintf("with stuff to keep at end\r\n");
1042 #endif /* DEBUG_UPDATE */
1043 /*
1044 * We have to recalculate fx here because we set it
1045 * to zero above as a flag saying that we hadn't done
1046 * an early first insert.
1047 */
1048 fx = (int) ((nsb - nfd) - (osb - ofd));
1049 if (fx > 0) {
1050 /*
1051 * insert fx chars of new starting at nfd
1052 */
1053 #ifdef DEBUG_UPDATE
1054 if (!T_CanIns)
1055 reprintf(" ERROR: cannot insert in late first diff\n");
1056 #endif /* DEBUG_UPDATE */
1057 Insert_write(nfd, fx);
1058 str_insert(old, (int) (ofd - old), TermH, nfd, fx);
1059 }
1060
1061 /*
1062 * write (nsb-nfd) - fx chars of new starting at (nfd + fx)
1063 */
1064 so_write(nfd + fx, (nsb - nfd) - fx);
1065 str_cp(ofd + fx, nfd + fx, (int) ((nsb - nfd) - fx));
1066 }
1067 else {
1068 #ifdef DEBUG_UPDATE
1069 reprintf("without anything to save\r\n");
1070 #endif /* DEBUG_UPDATE */
1071 so_write(nfd, (nsb - nfd));
1072 str_cp(ofd, nfd, (int) (nsb - nfd));
1073 }
1074 }
1075
1076 /*
1077 * line is now NEW up to nse
1078 */
1079 if (sx >= 0) {
1080 #ifdef DEBUG_UPDATE
1081 reprintf("second diff insert at %d...\r\n", nse - new);
1082 #endif /* DEBUG_UPDATE */
1083 MoveToChar(nse - new);
1084 if (ols != oe) {
1085 #ifdef DEBUG_UPDATE
1086 reprintf("with stuff to keep at end\r\n");
1087 #endif /* DEBUG_UPDATE */
1088 if (sx > 0) {
1089 /* insert sx chars of new starting at nse */
1090 #ifdef DEBUG_UPDATE
1091 if (!T_CanIns)
1092 reprintf(" ERROR: cannot insert in second diff\n");
1093 #endif /* DEBUG_UPDATE */
1094 Insert_write(nse, sx);
1095 }
1096
1097 /*
1098 * write (nls-nse) - sx chars of new starting at (nse + sx)
1099 */
1100 so_write(nse + sx, (nls - nse) - sx);
1101 }
1102 else {
1103 #ifdef DEBUG_UPDATE
1104 reprintf("without anything to save\r\n");
1105 #endif /* DEBUG_UPDATE */
1106 so_write(nse, (nls - nse));
1107
1108 /*
1109 * No need to do a clear-to-end here because we were doing
1110 * a second insert, so we will have over written all of the
1111 * old string.
1112 */
1113 }
1114 }
1115 #ifdef DEBUG_UPDATE
1116 reprintf("done.\r\n");
1117 #endif /* DEBUG_UPDATE */
1118 }
1119
1120
1121 static void
cpy_pad_spaces(Char * dst,Char * src,int width)1122 cpy_pad_spaces(Char *dst, Char *src, int width)
1123 {
1124 int i;
1125
1126 for (i = 0; i < width; i++) {
1127 if (*src == (Char) 0)
1128 break;
1129 *dst++ = *src++;
1130 }
1131
1132 while (i < width) {
1133 *dst++ = ' ';
1134 i++;
1135 }
1136 *dst = (Char) 0;
1137 }
1138
1139 void
RefCursor(void)1140 RefCursor(void)
1141 { /* only move to new cursor pos */
1142 Char *cp;
1143 int w, h, th, v;
1144
1145 /* first we must find where the cursor is... */
1146 h = 0;
1147 v = 0;
1148 th = TermH; /* optimize for speed */
1149
1150 for (cp = Prompt; cp != NULL && *cp; ) { /* do prompt */
1151 if (*cp & LITERAL) {
1152 cp++;
1153 continue;
1154 }
1155 w = NLSClassify(*cp & CHAR, cp == Prompt, 0);
1156 cp++;
1157 switch(w) {
1158 case NLSCLASS_NL:
1159 h = 0;
1160 v++;
1161 break;
1162 case NLSCLASS_TAB:
1163 while (++h & 07)
1164 ;
1165 break;
1166 case NLSCLASS_CTRL:
1167 h += 2;
1168 break;
1169 case NLSCLASS_ILLEGAL:
1170 h += 4;
1171 break;
1172 case NLSCLASS_ILLEGAL2:
1173 case NLSCLASS_ILLEGAL3:
1174 case NLSCLASS_ILLEGAL4:
1175 h += 3 + 2 * NLSCLASS_ILLEGAL_SIZE(w);
1176 break;
1177 default:
1178 h += w;
1179 }
1180 if (h >= th) { /* check, extra long tabs picked up here also */
1181 h -= th;
1182 v++;
1183 }
1184 }
1185
1186 for (cp = InputBuf; cp < Cursor;) { /* do input buffer to Cursor */
1187 w = NLSClassify(*cp & CHAR, cp == InputBuf, 0);
1188 cp++;
1189 switch(w) {
1190 case NLSCLASS_NL:
1191 h = 0;
1192 v++;
1193 break;
1194 case NLSCLASS_TAB:
1195 while (++h & 07)
1196 ;
1197 break;
1198 case NLSCLASS_CTRL:
1199 h += 2;
1200 break;
1201 case NLSCLASS_ILLEGAL:
1202 h += 4;
1203 break;
1204 case NLSCLASS_ILLEGAL2:
1205 case NLSCLASS_ILLEGAL3:
1206 case NLSCLASS_ILLEGAL4:
1207 h += 3 + 2 * NLSCLASS_ILLEGAL_SIZE(w);
1208 break;
1209 default:
1210 h += w;
1211 }
1212 if (h >= th) { /* check, extra long tabs picked up here also */
1213 h -= th;
1214 v++;
1215 }
1216 }
1217
1218 /* now go there */
1219 MoveToLine(v);
1220 MoveToChar(h);
1221 if (adrof(STRhighlight) && MarkIsSet) {
1222 ClearLines();
1223 ClearDisp();
1224 Refresh();
1225 }
1226 flush();
1227 }
1228
1229 #ifndef WINTT_NATIVE
1230 static void
PutPlusOne(Char c,int width)1231 PutPlusOne(Char c, int width)
1232 {
1233 while (width > 1 && CursorH + width > TermH)
1234 PutPlusOne(' ', 1);
1235 if ((c & LITERAL) != 0) {
1236 Char *d;
1237 for (d = litptr + (c & ~LITERAL) * LIT_FACTOR; *d; d++)
1238 (void) putwraw(*d);
1239 } else {
1240 (void) putwraw(c);
1241 }
1242 Display[CursorV][CursorH++] = (Char) c;
1243 while (--width > 0)
1244 Display[CursorV][CursorH++] = CHAR_DBWIDTH;
1245 if (CursorH >= TermH) { /* if we must overflow */
1246 CursorH = 0;
1247 CursorV++;
1248 OldvcV++;
1249 if (T_Margin & MARGIN_AUTO) {
1250 if (T_Margin & MARGIN_MAGIC) {
1251 (void) putraw(' ');
1252 (void) putraw('\b');
1253 }
1254 }
1255 else {
1256 (void) putraw('\r');
1257 (void) putraw('\n');
1258 }
1259 }
1260 }
1261 #endif
1262
1263 void
RefPlusOne(int l)1264 RefPlusOne(int l)
1265 { /* we added just one char, handle it fast.
1266 * assumes that screen cursor == real cursor */
1267 Char *cp, c;
1268 int w;
1269
1270 if (Cursor != LastChar) {
1271 Refresh(); /* too hard to handle */
1272 return;
1273 }
1274 if (rprompt_h != 0 && (TermH - CursorH - rprompt_h < 3)) {
1275 Refresh(); /* clear out rprompt if less than one char gap*/
1276 return;
1277 }
1278 cp = Cursor - l;
1279 c = *cp & CHAR;
1280 w = NLSClassify(c, cp == InputBuf, 0);
1281 switch(w) {
1282 case NLSCLASS_CTRL:
1283 PutPlusOne('^', 1);
1284 if (c == CTL_ESC('\177')) {
1285 PutPlusOne('?', 1);
1286 break;
1287 }
1288 #ifdef IS_ASCII
1289 /* uncontrolify it; works only for iso8859-1 like sets */
1290 PutPlusOne((c | 0100), 1);
1291 #else
1292 PutPlusOne(_toebcdic[_toascii[c]|0100], 1);
1293 #endif
1294 break;
1295 case NLSCLASS_ILLEGAL:
1296 PutPlusOne('\\', 1);
1297 PutPlusOne(((c >> 6) & 7) + '0', 1);
1298 PutPlusOne(((c >> 3) & 7) + '0', 1);
1299 PutPlusOne((c & 7) + '0', 1);
1300 break;
1301 case 1:
1302 if (adrof(STRhighlight) && MarkIsSet)
1303 StartHighlight();
1304 if (l > 1)
1305 PutPlusOne(MakeLiteral(cp, l, 0), 1);
1306 else
1307 PutPlusOne(*cp, 1);
1308 if (adrof(STRhighlight) && MarkIsSet)
1309 StopHighlight();
1310 break;
1311 default:
1312 Refresh(); /* too hard to handle */
1313 return;
1314 }
1315 flush();
1316 }
1317
1318 /* clear the screen buffers so that new new prompt starts fresh. */
1319
1320 void
ClearDisp(void)1321 ClearDisp(void)
1322 {
1323 int i;
1324
1325 CursorV = 0; /* clear the display buffer */
1326 CursorH = 0;
1327 for (i = 0; i < TermV; i++)
1328 (void) memset(Display[i], 0, (TermH + 1) * sizeof(Display[0][0]));
1329 OldvcV = 0;
1330 litlen = 0;
1331 }
1332
1333 void
ClearLines(void)1334 ClearLines(void)
1335 { /* Make sure all lines are *really* blank */
1336 int i;
1337
1338 if (T_CanCEOL) {
1339 /*
1340 * Clear the lines from the bottom up so that if we try moving
1341 * the cursor down by writing the character that is at the end
1342 * of the screen line, we won't rewrite a character that shouldn't
1343 * be there.
1344 */
1345 for (i = OldvcV; i >= 0; i--) { /* for each line on the screen */
1346 MoveToLine(i);
1347 MoveToChar(0);
1348 ClearEOL(TermH);
1349 }
1350 }
1351 else {
1352 MoveToLine(OldvcV); /* go to last line */
1353 (void) putraw('\r'); /* go to BOL */
1354 (void) putraw('\n'); /* go to new line */
1355 }
1356 }
1357