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