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