gzip.1 & co.: Please keep DragonFly timeline for .Dd, not FreeBSD's.
[dragonfly.git] / contrib / tcsh-6 / ed.refresh.c
blobafba75ec5169bdf85f0e4f4bbe1e28c74c119875
1 /* $Header: /p/tcsh/cvsroot/tcsh/ed.refresh.c,v 3.46 2006/08/23 15:03:14 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.
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.
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.
33 #include "sh.h"
35 RCSID("$tcsh: ed.refresh.c,v 3.46 2006/08/23 15:03:14 christos Exp $")
37 #include "ed.h"
38 /* #define DEBUG_UPDATE */
39 /* #define DEBUG_REFRESH */
40 /* #define DEBUG_LITERAL */
42 /* refresh.c -- refresh the current set of lines on the screen */
44 Char *litptr;
45 static int vcursor_h, vcursor_v;
46 static int rprompt_h, rprompt_v;
48 static int MakeLiteral (Char *, int, Char);
49 static int Draw (Char *, 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 dprintf (char *, ...);
65 #ifdef DEBUG_UPDATE
66 static void dprintstr (char *, const Char *, const Char *);
68 static void
69 dprintstr(char *str, const Char *f, const Char *t)
71 dprintf("%s:\"", str);
72 while (f < t) {
73 if (ASC(*f) & ~ASCII)
74 dprintf("[%x]", *f++);
75 else
76 dprintf("%c", CTL_ESC(ASCII & ASC(*f++)));
78 dprintf("\"\r\n");
80 #endif /* DEBUG_UPDATE */
82 /* dprintf():
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!
87 static void
88 dprintf(char *fmt, ...)
90 static int fd = -1;
91 char *dtty;
93 if ((dtty = getenv("DEBUGTTY"))) {
94 int o;
95 va_list va;
96 va_start(va, fmt);
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;
109 #endif /* DEBUG_UPDATE || DEBUG_REFRESH || DEBUG_LITERAL */
111 static int litlen = 0, litalloc = 0;
113 static int MakeLiteral(Char *str, int len, Char addlit)
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;
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;
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;
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;
154 Strncpy(litptr + litlen, str, len);
155 litlen += len;
157 litptr[litlen++] = 0;
158 while (litlen % LIT_FACTOR);
159 return i | LITERAL;
162 static int
163 Draw(Char *cp, int nocomb) /* draw char at cp, expand tabs, ctl chars */
165 int w, i, lv, lh;
166 Char c, attr;
168 attr = *cp & ~CHAR;
169 c = *cp & CHAR;
170 w = NLSClassify(c, nocomb);
171 switch (w) {
172 case NLSCLASS_NL:
173 Vdraw('\0', 0); /* assure end of line */
174 vcursor_h = 0; /* reset cursor pos */
175 vcursor_v++;
176 break;
177 case NLSCLASS_TAB:
178 do {
179 Vdraw(' ', 1);
180 } while ((vcursor_h & 07) != 0);
181 break;
182 case NLSCLASS_CTRL:
183 Vdraw('^' | attr, 1);
184 if (c == CTL_ESC('\177')) {
185 Vdraw('?' | attr, 1);
186 } else {
187 #ifdef IS_ASCII
188 /* uncontrolify it; works only for iso8859-1 like sets */
189 Vdraw(c | 0100 | attr, 1);
190 #else
191 Vdraw(_toebcdic[_toascii[c]|0100] | attr, 1);
192 #endif
194 break;
195 case NLSCLASS_ILLEGAL:
196 Vdraw('\\' | attr, 1);
197 Vdraw((((c >> 6) & 7) + '0') | attr, 1);
198 Vdraw((((c >> 3) & 7) + '0') | attr, 1);
199 Vdraw(((c & 7) + '0') | attr, 1);
200 break;
201 case NLSCLASS_ILLEGAL2:
202 case NLSCLASS_ILLEGAL3:
203 case NLSCLASS_ILLEGAL4:
204 Vdraw('\\' | attr, 1);
205 Vdraw('U' | attr, 1);
206 Vdraw('+' | attr, 1);
207 for (i = 8 * NLSCLASS_ILLEGAL_SIZE(w) - 4; i >= 0; i -= 4)
208 Vdraw("0123456789ABCDEF"[(c >> i) & 15] | attr, 1);
209 break;
210 case 0:
211 lv = vcursor_v;
212 lh = vcursor_h;
213 for (;;) {
214 lh--;
215 if (lh < 0) {
216 lv--;
217 if (lv < 0)
218 break;
219 lh = Strlen(Vdisplay[lv]) - 1;
221 if (Vdisplay[lv][lh] != CHAR_DBWIDTH)
222 break;
224 if (lv < 0) {
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;
231 Vdisplay[lv][lh] = MakeLiteral(cp, 1, Vdisplay[lv][lh]);
232 break;
233 default:
234 Vdraw(*cp, w);
235 break;
237 return 1;
240 static void
241 Vdraw(Char c, int width) /* draw char c onto V lines */
243 #ifdef DEBUG_REFRESH
244 # ifdef SHORT_STRINGS
245 dprintf("Vdrawing %6.6o '%c' %d\r\n", (unsigned)c, (int)(c & ASCII), width);
246 # else
247 dprintf("Vdrawing %3.3o '%c' %d\r\n", (unsigned)c, (int)c, width);
248 # endif /* SHORT_STRNGS */
249 #endif /* DEBUG_REFRESH */
251 /* Hopefully this is what all the terminals do with multi-column characters
252 that "span line breaks". */
253 while (vcursor_h + width > TermH)
254 Vdraw(' ', 1);
255 Vdisplay[vcursor_v][vcursor_h] = c;
256 if (width)
257 vcursor_h++; /* advance to next place */
258 while (--width > 0)
259 Vdisplay[vcursor_v][vcursor_h++] = CHAR_DBWIDTH;
260 if (vcursor_h >= TermH) {
261 Vdisplay[vcursor_v][TermH] = '\0'; /* assure end of line */
262 vcursor_h = 0; /* reset it. */
263 vcursor_v++;
264 #ifdef DEBUG_REFRESH
265 if (vcursor_v >= TermV) { /* should NEVER happen. */
266 dprintf("\r\nVdraw: vcursor_v overflow! Vcursor_v == %d > %d\r\n",
267 vcursor_v, TermV);
268 abort();
270 #endif /* DEBUG_REFRESH */
275 * RefreshPromptpart()
276 * draws a prompt element, expanding literals (we know it's ASCIZ)
278 static void
279 RefreshPromptpart(Char *buf)
281 Char *cp;
282 int w;
284 if (buf == NULL)
285 return;
286 for (cp = buf; *cp; ) {
287 if (*cp & LITERAL) {
288 Char *litstart = cp;
289 while (*cp & LITERAL)
290 cp++;
291 if (*cp) {
292 w = NLSWidth(*cp & CHAR);
293 Vdraw(MakeLiteral(litstart, cp + 1 - litstart, 0), w);
294 cp++;
296 else {
298 * XXX: This is a bug, we lose the last literal, if it is not
299 * followed by a normal character, but it is too hard to fix
301 break;
304 else
305 cp += Draw(cp, cp == buf);
310 * Refresh()
311 * draws the new virtual screen image from the current input
312 * line, then goes line-by-line changing the real image to the new
313 * virtual image. The routine to re-draw a line can be replaced
314 * easily in hopes of a smarter one being placed there.
316 #ifndef WINNT_NATIVE
317 static
318 #endif
319 int OldvcV = 0;
321 void
322 Refresh(void)
324 int cur_line;
325 Char *cp;
326 int cur_h, cur_v = 0, new_vcv;
327 int rhdiff;
328 Char oldgetting;
330 #ifdef DEBUG_REFRESH
331 dprintf("Prompt = :%s:\r\n", short2str(Prompt));
332 dprintf("InputBuf = :%s:\r\n", short2str(InputBuf));
333 #endif /* DEBUG_REFRESH */
334 oldgetting = GettingInput;
335 GettingInput = 0; /* avoid re-entrance via SIGWINCH */
337 /* reset the Vdraw cursor, temporarily draw rprompt to calculate its size */
338 vcursor_h = 0;
339 vcursor_v = 0;
340 RefreshPromptpart(RPrompt);
341 rprompt_h = vcursor_h;
342 rprompt_v = vcursor_v;
344 /* reset the Vdraw cursor, draw prompt */
345 vcursor_h = 0;
346 vcursor_v = 0;
347 RefreshPromptpart(Prompt);
348 cur_h = -1; /* set flag in case I'm not set */
350 /* draw the current input buffer */
351 for (cp = InputBuf; (cp < LastChar); ) {
352 if (cp >= Cursor && cur_h == -1) {
353 cur_h = vcursor_h; /* save for later */
354 cur_v = vcursor_v;
355 Cursor = cp;
357 cp += Draw(cp, cp == InputBuf);
360 if (cur_h == -1) { /* if I haven't been set yet, I'm at the end */
361 cur_h = vcursor_h;
362 cur_v = vcursor_v;
365 rhdiff = TermH - vcursor_h - rprompt_h;
366 if (rprompt_h != 0 && rprompt_v == 0 && vcursor_v == 0 && rhdiff > 1) {
368 * have a right-hand side prompt that will fit on
369 * the end of the first line with at least one
370 * character gap to the input buffer.
372 while (--rhdiff > 0) /* pad out with spaces */
373 Vdraw(' ', 1);
374 RefreshPromptpart(RPrompt);
376 else {
377 rprompt_h = 0; /* flag "not using rprompt" */
378 rprompt_v = 0;
381 new_vcv = vcursor_v; /* must be done BEFORE the NUL is written */
382 Vdraw('\0', 1); /* put NUL on end */
384 #if defined (DEBUG_REFRESH)
385 dprintf("TermH=%d, vcur_h=%d, vcur_v=%d, Vdisplay[0]=\r\n:%80.80s:\r\n",
386 TermH, vcursor_h, vcursor_v, short2str(Vdisplay[0]));
387 #endif /* DEBUG_REFRESH */
389 #ifdef DEBUG_UPDATE
390 dprintf("updating %d lines.\r\n", new_vcv);
391 #endif /* DEBUG_UPDATE */
392 for (cur_line = 0; cur_line <= new_vcv; cur_line++) {
393 /* NOTE THAT update_line MAY CHANGE Display[cur_line] */
394 update_line(Display[cur_line], Vdisplay[cur_line], cur_line);
395 #ifdef WINNT_NATIVE
396 flush();
397 #endif /* WINNT_NATIVE */
400 * Copy the new line to be the current one, and pad out with spaces
401 * to the full width of the terminal so that if we try moving the
402 * cursor by writing the character that is at the end of the
403 * screen line, it won't be a NUL or some old leftover stuff.
405 cpy_pad_spaces(Display[cur_line], Vdisplay[cur_line], TermH);
407 #ifdef DEBUG_REFRESH
408 dprintf("\r\nvcursor_v = %d, OldvcV = %d, cur_line = %d\r\n",
409 vcursor_v, OldvcV, cur_line);
410 #endif /* DEBUG_REFRESH */
411 if (OldvcV > new_vcv) {
412 for (; cur_line <= OldvcV; cur_line++) {
413 update_line(Display[cur_line], STRNULL, cur_line);
414 *Display[cur_line] = '\0';
417 OldvcV = new_vcv; /* set for next time */
418 #ifdef DEBUG_REFRESH
419 dprintf("\r\nCursorH = %d, CursorV = %d, cur_h = %d, cur_v = %d\r\n",
420 CursorH, CursorV, cur_h, cur_v);
421 #endif /* DEBUG_REFRESH */
422 #ifdef WINNT_NATIVE
423 flush();
424 #endif /* WINNT_NATIVE */
425 MoveToLine(cur_v); /* go to where the cursor is */
426 MoveToChar(cur_h);
427 SetAttributes(0); /* Clear all attributes */
428 flush(); /* send the output... */
429 GettingInput = oldgetting; /* reset to old value */
432 #ifdef notdef
433 GotoBottom(void)
434 { /* used to go to last used screen line */
435 MoveToLine(OldvcV);
438 #endif
440 void
441 PastBottom(void)
442 { /* used to go to last used screen line */
443 MoveToLine(OldvcV);
444 (void) putraw('\r');
445 (void) putraw('\n');
446 ClearDisp();
447 flush();
451 /* insert num characters of s into d (in front of the character) at dat,
452 maximum length of d is dlen */
453 static void
454 str_insert(Char *d, int dat, int dlen, Char *s, int num)
456 Char *a, *b;
458 if (num <= 0)
459 return;
460 if (num > dlen - dat)
461 num = dlen - dat;
463 #ifdef DEBUG_REFRESH
464 dprintf("str_insert() starting: %d at %d max %d, d == \"%s\"\n",
465 num, dat, dlen, short2str(d));
466 dprintf("s == \"%s\"n", short2str(s));
467 #endif /* DEBUG_REFRESH */
469 /* open up the space for num chars */
470 if (num > 0) {
471 b = d + dlen - 1;
472 a = b - num;
473 while (a >= &d[dat])
474 *b-- = *a--;
475 d[dlen] = '\0'; /* just in case */
477 #ifdef DEBUG_REFRESH
478 dprintf("str_insert() after insert: %d at %d max %d, d == \"%s\"\n",
479 num, dat, dlen, short2str(d));
480 dprintf("s == \"%s\"n", short2str(s));
481 #endif /* DEBUG_REFRESH */
483 /* copy the characters */
484 for (a = d + dat; (a < d + dlen) && (num > 0); num--)
485 *a++ = *s++;
487 #ifdef DEBUG_REFRESH
488 dprintf("str_insert() after copy: %d at %d max %d, d == \"%s\"\n",
489 num, dat, dlen, d, short2str(s));
490 dprintf("s == \"%s\"n", short2str(s));
491 #endif /* DEBUG_REFRESH */
494 /* delete num characters d at dat, maximum length of d is dlen */
495 static void
496 str_delete(Char *d, int dat, int dlen, int num)
498 Char *a, *b;
500 if (num <= 0)
501 return;
502 if (dat + num >= dlen) {
503 d[dat] = '\0';
504 return;
507 #ifdef DEBUG_REFRESH
508 dprintf("str_delete() starting: %d at %d max %d, d == \"%s\"\n",
509 num, dat, dlen, short2str(d));
510 #endif /* DEBUG_REFRESH */
512 /* open up the space for num chars */
513 if (num > 0) {
514 b = d + dat;
515 a = b + num;
516 while (a < &d[dlen])
517 *b++ = *a++;
518 d[dlen] = '\0'; /* just in case */
520 #ifdef DEBUG_REFRESH
521 dprintf("str_delete() after delete: %d at %d max %d, d == \"%s\"\n",
522 num, dat, dlen, short2str(d));
523 #endif /* DEBUG_REFRESH */
526 static void
527 str_cp(Char *a, Char *b, int n)
529 while (n-- && *b)
530 *a++ = *b++;
534 /* ****************************************************************
535 update_line() is based on finding the middle difference of each line
536 on the screen; vis:
538 /old first difference
539 /beginning of line | /old last same /old EOL
540 v v v v
541 old: eddie> Oh, my little gruntle-buggy is to me, as lurgid as
542 new: eddie> Oh, my little buggy says to me, as lurgid as
543 ^ ^ ^ ^
544 \beginning of line | \new last same \new end of line
545 \new first difference
547 all are character pointers for the sake of speed. Special cases for
548 no differences, as well as for end of line additions must be handled.
549 **************************************************************** */
551 /* Minimum at which doing an insert it "worth it". This should be about
552 * half the "cost" of going into insert mode, inserting a character, and
553 * going back out. This should really be calculated from the termcap
554 * data... For the moment, a good number for ANSI terminals.
556 #define MIN_END_KEEP 4
558 static void /* could be changed to make it smarter */
559 update_line(Char *old, Char *new, int cur_line)
561 Char *o, *n, *p, c;
562 Char *ofd, *ols, *oe, *nfd, *nls, *ne;
563 Char *osb, *ose, *nsb, *nse;
564 int fx, sx;
567 * find first diff (won't be CHAR_DBWIDTH in either line)
569 for (o = old, n = new; *o && (*o == *n); o++, n++)
570 continue;
571 ofd = o;
572 nfd = n;
575 * Find the end of both old and new
577 o = Strend(o);
580 * Remove any trailing blanks off of the end, being careful not to
581 * back up past the beginning.
583 if (!(adrof(STRhighlight) && MarkIsSet)) {
584 while (ofd < o) {
585 if (o[-1] != ' ')
586 break;
587 o--;
590 oe = o;
591 *oe = (Char) 0;
593 n = Strend(n);
595 /* remove blanks from end of new */
596 if (!(adrof(STRhighlight) && MarkIsSet)) {
597 while (nfd < n) {
598 if (n[-1] != ' ')
599 break;
600 n--;
603 ne = n;
604 *ne = (Char) 0;
607 * if no diff, continue to next line of redraw
609 if (*ofd == '\0' && *nfd == '\0') {
610 #ifdef DEBUG_UPDATE
611 dprintf("no difference.\r\n");
612 #endif /* DEBUG_UPDATE */
613 return;
617 * find last same pointer
619 while ((o > ofd) && (n > nfd) && (*--o == *--n))
620 continue;
621 if (*o != *n) {
622 o++;
623 n++;
625 while (*o == CHAR_DBWIDTH) {
626 o++;
627 n++;
629 ols = o;
630 nls = n;
633 * find same begining and same end
635 osb = ols;
636 nsb = nls;
637 ose = ols;
638 nse = nls;
641 * case 1: insert: scan from nfd to nls looking for *ofd
643 if (*ofd) {
644 for (c = *ofd, n = nfd; n < nls; n++) {
645 if (c == *n) {
646 for (o = ofd, p = n; p < nls && o < ols && *o == *p; o++, p++)
647 continue;
649 * if the new match is longer and it's worth keeping, then we
650 * take it
652 if (((nse - nsb) < (p - n)) && (2 * (p - n) > n - nfd)) {
653 nsb = n;
654 nse = p;
655 osb = ofd;
656 ose = o;
663 * case 2: delete: scan from ofd to ols looking for *nfd
665 if (*nfd) {
666 for (c = *nfd, o = ofd; o < ols; o++) {
667 if (c == *o) {
668 for (n = nfd, p = o; p < ols && n < nls && *p == *n; p++, n++)
669 continue;
671 * if the new match is longer and it's worth keeping, then we
672 * take it
674 if (((ose - osb) < (p - o)) && (2 * (p - o) > o - ofd)) {
675 nsb = nfd;
676 nse = n;
677 osb = o;
678 ose = p;
683 #ifdef notdef
685 * If `last same' is before `same end' re-adjust
687 if (ols < ose)
688 ols = ose;
689 if (nls < nse)
690 nls = nse;
691 #endif
694 * Pragmatics I: If old trailing whitespace or not enough characters to
695 * save to be worth it, then don't save the last same info.
697 if ((oe - ols) < MIN_END_KEEP) {
698 ols = oe;
699 nls = ne;
703 * Pragmatics II: if the terminal isn't smart enough, make the data dumber
704 * so the smart update doesn't try anything fancy
708 * fx is the number of characters we need to insert/delete: in the
709 * beginning to bring the two same begins together
711 fx = (int) ((nsb - nfd) - (osb - ofd));
713 * sx is the number of characters we need to insert/delete: in the end to
714 * bring the two same last parts together
716 sx = (int) ((nls - nse) - (ols - ose));
718 if (!T_CanIns) {
719 if (fx > 0) {
720 osb = ols;
721 ose = ols;
722 nsb = nls;
723 nse = nls;
725 if (sx > 0) {
726 ols = oe;
727 nls = ne;
729 if ((ols - ofd) < (nls - nfd)) {
730 ols = oe;
731 nls = ne;
734 if (!T_CanDel) {
735 if (fx < 0) {
736 osb = ols;
737 ose = ols;
738 nsb = nls;
739 nse = nls;
741 if (sx < 0) {
742 ols = oe;
743 nls = ne;
745 if ((ols - ofd) > (nls - nfd)) {
746 ols = oe;
747 nls = ne;
752 * Pragmatics III: make sure the middle shifted pointers are correct if
753 * they don't point to anything (we may have moved ols or nls).
755 /* if the change isn't worth it, don't bother */
756 /* was: if (osb == ose) */
757 if ((ose - osb) < MIN_END_KEEP) {
758 osb = ols;
759 ose = ols;
760 nsb = nls;
761 nse = nls;
765 * Now that we are done with pragmatics we recompute fx, sx
767 fx = (int) ((nsb - nfd) - (osb - ofd));
768 sx = (int) ((nls - nse) - (ols - ose));
770 #ifdef DEBUG_UPDATE
771 dprintf("\n");
772 dprintf("ofd %d, osb %d, ose %d, ols %d, oe %d\n",
773 ofd - old, osb - old, ose - old, ols - old, oe - old);
774 dprintf("nfd %d, nsb %d, nse %d, nls %d, ne %d\n",
775 nfd - new, nsb - new, nse - new, nls - new, ne - new);
776 dprintf("xxx-xxx:\"00000000001111111111222222222233333333334\"\r\n");
777 dprintf("xxx-xxx:\"01234567890123456789012345678901234567890\"\r\n");
778 dprintstr("old- oe", old, oe);
779 dprintstr("new- ne", new, ne);
780 dprintstr("old-ofd", old, ofd);
781 dprintstr("new-nfd", new, nfd);
782 dprintstr("ofd-osb", ofd, osb);
783 dprintstr("nfd-nsb", nfd, nsb);
784 dprintstr("osb-ose", osb, ose);
785 dprintstr("nsb-nse", nsb, nse);
786 dprintstr("ose-ols", ose, ols);
787 dprintstr("nse-nls", nse, nls);
788 dprintstr("ols- oe", ols, oe);
789 dprintstr("nls- ne", nls, ne);
790 #endif /* DEBUG_UPDATE */
793 * CursorV to this line cur_line MUST be in this routine so that if we
794 * don't have to change the line, we don't move to it. CursorH to first
795 * diff char
797 MoveToLine(cur_line);
800 * at this point we have something like this:
802 * /old /ofd /osb /ose /ols /oe
803 * v.....................v v..................v v........v
804 * eddie> Oh, my fredded gruntle-buggy is to me, as foo var lurgid as
805 * eddie> Oh, my fredded quiux buggy is to me, as gruntle-lurgid as
806 * ^.....................^ ^..................^ ^........^
807 * \new \nfd \nsb \nse \nls \ne
809 * fx is the difference in length between the the chars between nfd and
810 * nsb, and the chars between ofd and osb, and is thus the number of
811 * characters to delete if < 0 (new is shorter than old, as above),
812 * or insert (new is longer than short).
814 * sx is the same for the second differences.
818 * if we have a net insert on the first difference, AND inserting the net
819 * amount ((nsb-nfd) - (osb-ofd)) won't push the last useful character
820 * (which is ne if nls != ne, otherwise is nse) off the edge of the screen
821 * (TermH - 1) else we do the deletes first so that we keep everything we
822 * need to.
826 * if the last same is the same like the end, there is no last same part,
827 * otherwise we want to keep the last same part set p to the last useful
828 * old character
830 p = (ols != oe) ? oe : ose;
833 * if (There is a diffence in the beginning) && (we need to insert
834 * characters) && (the number of characters to insert is less than the term
835 * width) We need to do an insert! else if (we need to delete characters)
836 * We need to delete characters! else No insert or delete
838 if ((nsb != nfd) && fx > 0 && ((p - old) + fx < TermH)) {
839 #ifdef DEBUG_UPDATE
840 dprintf("first diff insert at %d...\r\n", nfd - new);
841 #endif /* DEBUG_UPDATE */
843 * Move to the first char to insert, where the first diff is.
845 MoveToChar(nfd - new);
847 * Check if we have stuff to keep at end
849 if (nsb != ne) {
850 #ifdef DEBUG_UPDATE
851 dprintf("with stuff to keep at end\r\n");
852 #endif /* DEBUG_UPDATE */
854 * insert fx chars of new starting at nfd
856 if (fx > 0) {
857 #ifdef DEBUG_UPDATE
858 if (!T_CanIns)
859 dprintf(" ERROR: cannot insert in early first diff\n");
860 #endif /* DEBUG_UPDATE */
861 Insert_write(nfd, fx);
862 str_insert(old, (int) (ofd - old), TermH, nfd, fx);
865 * write (nsb-nfd) - fx chars of new starting at (nfd + fx)
867 so_write(nfd + fx, (nsb - nfd) - fx);
868 str_cp(ofd + fx, nfd + fx, (int) ((nsb - nfd) - fx));
870 else {
871 #ifdef DEBUG_UPDATE
872 dprintf("without anything to save\r\n");
873 #endif /* DEBUG_UPDATE */
874 so_write(nfd, (nsb - nfd));
875 str_cp(ofd, nfd, (int) (nsb - nfd));
877 * Done
879 return;
882 else if (fx < 0) {
883 #ifdef DEBUG_UPDATE
884 dprintf("first diff delete at %d...\r\n", ofd - old);
885 #endif /* DEBUG_UPDATE */
887 * move to the first char to delete where the first diff is
889 MoveToChar(ofd - old);
891 * Check if we have stuff to save
893 if (osb != oe) {
894 #ifdef DEBUG_UPDATE
895 dprintf("with stuff to save at end\r\n");
896 #endif /* DEBUG_UPDATE */
898 * fx is less than zero *always* here but we check for code
899 * symmetry
901 if (fx < 0) {
902 #ifdef DEBUG_UPDATE
903 if (!T_CanDel)
904 dprintf(" ERROR: cannot delete in first diff\n");
905 #endif /* DEBUG_UPDATE */
906 DeleteChars(-fx);
907 str_delete(old, (int) (ofd - old), TermH, -fx);
910 * write (nsb-nfd) chars of new starting at nfd
912 so_write(nfd, (nsb - nfd));
913 str_cp(ofd, nfd, (int) (nsb - nfd));
916 else {
917 #ifdef DEBUG_UPDATE
918 dprintf("but with nothing left to save\r\n");
919 #endif /* DEBUG_UPDATE */
921 * write (nsb-nfd) chars of new starting at nfd
923 so_write(nfd, (nsb - nfd));
924 #ifdef DEBUG_REFRESH
925 dprintf("cleareol %d\n", (oe - old) - (ne - new));
926 #endif /* DEBUG_UPDATE */
927 #ifndef WINNT_NATIVE
928 ClearEOL((oe - old) - (ne - new));
929 #else
931 * The calculation above does not work too well on NT
933 ClearEOL(TermH - CursorH);
934 #endif /*WINNT_NATIVE*/
936 * Done
938 return;
941 else
942 fx = 0;
944 if (sx < 0) {
945 #ifdef DEBUG_UPDATE
946 dprintf("second diff delete at %d...\r\n", (ose - old) + fx);
947 #endif /* DEBUG_UPDATE */
949 * Check if we have stuff to delete
952 * fx is the number of characters inserted (+) or deleted (-)
955 MoveToChar((ose - old) + fx);
957 * Check if we have stuff to save
959 if (ols != oe) {
960 #ifdef DEBUG_UPDATE
961 dprintf("with stuff to save at end\r\n");
962 #endif /* DEBUG_UPDATE */
964 * Again a duplicate test.
966 if (sx < 0) {
967 #ifdef DEBUG_UPDATE
968 if (!T_CanDel)
969 dprintf(" ERROR: cannot delete in second diff\n");
970 #endif /* DEBUG_UPDATE */
971 DeleteChars(-sx);
975 * write (nls-nse) chars of new starting at nse
977 so_write(nse, (nls - nse));
979 else {
980 int olen = (int) (oe - old + fx);
981 if (olen > TermH)
982 olen = TermH;
983 #ifdef DEBUG_UPDATE
984 dprintf("but with nothing left to save\r\n");
985 #endif /* DEBUG_UPDATE */
986 so_write(nse, (nls - nse));
987 #ifdef DEBUG_REFRESH
988 dprintf("cleareol %d\n", olen - (ne - new));
989 #endif /* DEBUG_UPDATE */
990 #ifndef WINNT_NATIVE
991 ClearEOL(olen - (ne - new));
992 #else
994 * The calculation above does not work too well on NT
996 ClearEOL(TermH - CursorH);
997 #endif /*WINNT_NATIVE*/
1002 * if we have a first insert AND WE HAVEN'T ALREADY DONE IT...
1004 if ((nsb != nfd) && (osb - ofd) <= (nsb - nfd) && (fx == 0)) {
1005 #ifdef DEBUG_UPDATE
1006 dprintf("late first diff insert at %d...\r\n", nfd - new);
1007 #endif /* DEBUG_UPDATE */
1009 MoveToChar(nfd - new);
1011 * Check if we have stuff to keep at the end
1013 if (nsb != ne) {
1014 #ifdef DEBUG_UPDATE
1015 dprintf("with stuff to keep at end\r\n");
1016 #endif /* DEBUG_UPDATE */
1018 * We have to recalculate fx here because we set it
1019 * to zero above as a flag saying that we hadn't done
1020 * an early first insert.
1022 fx = (int) ((nsb - nfd) - (osb - ofd));
1023 if (fx > 0) {
1025 * insert fx chars of new starting at nfd
1027 #ifdef DEBUG_UPDATE
1028 if (!T_CanIns)
1029 dprintf(" ERROR: cannot insert in late first diff\n");
1030 #endif /* DEBUG_UPDATE */
1031 Insert_write(nfd, fx);
1032 str_insert(old, (int) (ofd - old), TermH, nfd, fx);
1036 * write (nsb-nfd) - fx chars of new starting at (nfd + fx)
1038 so_write(nfd + fx, (nsb - nfd) - fx);
1039 str_cp(ofd + fx, nfd + fx, (int) ((nsb - nfd) - fx));
1041 else {
1042 #ifdef DEBUG_UPDATE
1043 dprintf("without anything to save\r\n");
1044 #endif /* DEBUG_UPDATE */
1045 so_write(nfd, (nsb - nfd));
1046 str_cp(ofd, nfd, (int) (nsb - nfd));
1051 * line is now NEW up to nse
1053 if (sx >= 0) {
1054 #ifdef DEBUG_UPDATE
1055 dprintf("second diff insert at %d...\r\n", nse - new);
1056 #endif /* DEBUG_UPDATE */
1057 MoveToChar(nse - new);
1058 if (ols != oe) {
1059 #ifdef DEBUG_UPDATE
1060 dprintf("with stuff to keep at end\r\n");
1061 #endif /* DEBUG_UPDATE */
1062 if (sx > 0) {
1063 /* insert sx chars of new starting at nse */
1064 #ifdef DEBUG_UPDATE
1065 if (!T_CanIns)
1066 dprintf(" ERROR: cannot insert in second diff\n");
1067 #endif /* DEBUG_UPDATE */
1068 Insert_write(nse, sx);
1072 * write (nls-nse) - sx chars of new starting at (nse + sx)
1074 so_write(nse + sx, (nls - nse) - sx);
1076 else {
1077 #ifdef DEBUG_UPDATE
1078 dprintf("without anything to save\r\n");
1079 #endif /* DEBUG_UPDATE */
1080 so_write(nse, (nls - nse));
1083 * No need to do a clear-to-end here because we were doing
1084 * a second insert, so we will have over written all of the
1085 * old string.
1089 #ifdef DEBUG_UPDATE
1090 dprintf("done.\r\n");
1091 #endif /* DEBUG_UPDATE */
1095 static void
1096 cpy_pad_spaces(Char *dst, Char *src, int width)
1098 int i;
1100 for (i = 0; i < width; i++) {
1101 if (*src == (Char) 0)
1102 break;
1103 *dst++ = *src++;
1106 while (i < width) {
1107 *dst++ = ' ';
1108 i++;
1110 *dst = (Char) 0;
1113 void
1114 RefCursor(void)
1115 { /* only move to new cursor pos */
1116 Char *cp;
1117 int w, h, th, v;
1119 /* first we must find where the cursor is... */
1120 h = 0;
1121 v = 0;
1122 th = TermH; /* optimize for speed */
1124 for (cp = Prompt; cp != NULL && *cp; ) { /* do prompt */
1125 if (*cp & LITERAL) {
1126 cp++;
1127 continue;
1129 w = NLSClassify(*cp & CHAR, cp == Prompt);
1130 cp++;
1131 switch(w) {
1132 case NLSCLASS_NL:
1133 h = 0;
1134 v++;
1135 break;
1136 case NLSCLASS_TAB:
1137 while (++h & 07)
1139 break;
1140 case NLSCLASS_CTRL:
1141 h += 2;
1142 break;
1143 case NLSCLASS_ILLEGAL:
1144 h += 4;
1145 break;
1146 case NLSCLASS_ILLEGAL2:
1147 case NLSCLASS_ILLEGAL3:
1148 case NLSCLASS_ILLEGAL4:
1149 h += 3 + 2 * NLSCLASS_ILLEGAL_SIZE(w);
1150 break;
1151 default:
1152 h += w;
1154 if (h >= th) { /* check, extra long tabs picked up here also */
1155 h -= th;
1156 v++;
1160 for (cp = InputBuf; cp < Cursor;) { /* do input buffer to Cursor */
1161 w = NLSClassify(*cp & CHAR, cp == InputBuf);
1162 cp++;
1163 switch(w) {
1164 case NLSCLASS_NL:
1165 h = 0;
1166 v++;
1167 break;
1168 case NLSCLASS_TAB:
1169 while (++h & 07)
1171 break;
1172 case NLSCLASS_CTRL:
1173 h += 2;
1174 break;
1175 case NLSCLASS_ILLEGAL:
1176 h += 4;
1177 break;
1178 case NLSCLASS_ILLEGAL2:
1179 case NLSCLASS_ILLEGAL3:
1180 case NLSCLASS_ILLEGAL4:
1181 h += 3 + 2 * NLSCLASS_ILLEGAL_SIZE(w);
1182 break;
1183 default:
1184 h += w;
1186 if (h >= th) { /* check, extra long tabs picked up here also */
1187 h -= th;
1188 v++;
1192 /* now go there */
1193 MoveToLine(v);
1194 MoveToChar(h);
1195 if (adrof(STRhighlight) && MarkIsSet) {
1196 ClearLines();
1197 ClearDisp();
1198 Refresh();
1200 flush();
1203 #ifndef WINTT_NATIVE
1204 static void
1205 PutPlusOne(Char c, int width)
1207 while (width > 1 && CursorH + width > TermH)
1208 PutPlusOne(' ', 1);
1209 if ((c & LITERAL) != 0) {
1210 Char *d;
1211 for (d = litptr + (c & ~LITERAL) * LIT_FACTOR; *d; d++)
1212 (void) putwraw(*d);
1213 } else {
1214 (void) putwraw(c);
1216 Display[CursorV][CursorH++] = (Char) c;
1217 while (--width > 0)
1218 Display[CursorV][CursorH++] = CHAR_DBWIDTH;
1219 if (CursorH >= TermH) { /* if we must overflow */
1220 CursorH = 0;
1221 CursorV++;
1222 OldvcV++;
1223 if (T_Margin & MARGIN_AUTO) {
1224 if (T_Margin & MARGIN_MAGIC) {
1225 (void) putraw(' ');
1226 (void) putraw('\b');
1229 else {
1230 (void) putraw('\r');
1231 (void) putraw('\n');
1235 #endif
1237 void
1238 RefPlusOne(int l)
1239 { /* we added just one char, handle it fast.
1240 * assumes that screen cursor == real cursor */
1241 Char *cp, c;
1242 int w;
1244 if (Cursor != LastChar) {
1245 Refresh(); /* too hard to handle */
1246 return;
1248 if (rprompt_h != 0 && (TermH - CursorH - rprompt_h < 3)) {
1249 Refresh(); /* clear out rprompt if less than one char gap*/
1250 return;
1252 cp = Cursor - l;
1253 c = *cp & CHAR;
1254 w = NLSClassify(c, cp == InputBuf);
1255 switch(w) {
1256 case NLSCLASS_CTRL:
1257 PutPlusOne('^', 1);
1258 if (c == CTL_ESC('\177')) {
1259 PutPlusOne('?', 1);
1260 break;
1262 #ifdef IS_ASCII
1263 /* uncontrolify it; works only for iso8859-1 like sets */
1264 PutPlusOne((c | 0100), 1);
1265 #else
1266 PutPlusOne(_toebcdic[_toascii[c]|0100], 1);
1267 #endif
1268 break;
1269 case NLSCLASS_ILLEGAL:
1270 PutPlusOne('\\', 1);
1271 PutPlusOne(((c >> 6) & 7) + '0', 1);
1272 PutPlusOne(((c >> 3) & 7) + '0', 1);
1273 PutPlusOne((c & 7) + '0', 1);
1274 break;
1275 case 1:
1276 if (adrof(STRhighlight) && MarkIsSet)
1277 StartHighlight();
1278 if (l > 1)
1279 PutPlusOne(MakeLiteral(cp, l, 0), 1);
1280 else
1281 PutPlusOne(*cp, 1);
1282 if (adrof(STRhighlight) && MarkIsSet)
1283 StopHighlight();
1284 break;
1285 default:
1286 Refresh(); /* too hard to handle */
1287 return;
1289 flush();
1292 /* clear the screen buffers so that new new prompt starts fresh. */
1294 void
1295 ClearDisp(void)
1297 int i;
1299 CursorV = 0; /* clear the display buffer */
1300 CursorH = 0;
1301 for (i = 0; i < TermV; i++)
1302 (void) memset(Display[i], 0, TermH * sizeof(Display[0][0]));
1303 OldvcV = 0;
1304 litlen = 0;
1307 void
1308 ClearLines(void)
1309 { /* Make sure all lines are *really* blank */
1310 int i;
1312 if (T_CanCEOL) {
1314 * Clear the lines from the bottom up so that if we try moving
1315 * the cursor down by writing the character that is at the end
1316 * of the screen line, we won't rewrite a character that shouldn't
1317 * be there.
1319 for (i = OldvcV; i >= 0; i--) { /* for each line on the screen */
1320 MoveToLine(i);
1321 MoveToChar(0);
1322 ClearEOL(TermH);
1325 else {
1326 MoveToLine(OldvcV); /* go to last line */
1327 (void) putraw('\r'); /* go to BOL */
1328 (void) putraw('\n'); /* go to new line */