bnx: Remove unused code
[dragonfly.git] / contrib / tcsh-6 / ed.refresh.c
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.
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.46 2006/08/23 15:03:14 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);
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 *);
67
68 static void
69 dprintstr(char *str, const Char *f, const Char *t)
70 {
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++)));
77     }
78     dprintf("\"\r\n");
79 }
80 #endif /* DEBUG_UPDATE */
81
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!
86  */
87 static void
88 dprintf(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
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 static int
163 Draw(Char *cp, int nocomb)      /* draw char at cp, expand tabs, ctl chars */
164 {
165     int w, i, lv, lh;
166     Char c, attr;
167
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
193             }
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;
220                 }
221                 if (Vdisplay[lv][lh] != CHAR_DBWIDTH)
222                     break;
223             }
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;
230             }
231             Vdisplay[lv][lh] = MakeLiteral(cp, 1, Vdisplay[lv][lh]);
232             break;
233         default:
234             Vdraw(*cp, w);
235             break;
236     }
237     return 1;
238 }
239
240 static void
241 Vdraw(Char c, int width)        /* draw char c onto V lines */
242 {
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 */
250
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();
269         }
270 #endif /* DEBUG_REFRESH */
271     }
272 }
273
274 /*
275  *  RefreshPromptpart()
276  *      draws a prompt element, expanding literals (we know it's ASCIZ)
277  */
278 static void
279 RefreshPromptpart(Char *buf)
280 {
281     Char *cp;
282     int w;
283
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++;
295             }
296             else {
297                 /*
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
300                  */
301                 break;
302             }
303         }
304         else
305             cp += Draw(cp, cp == buf);
306     }
307 }
308
309 /*
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.
315  */
316 #ifndef WINNT_NATIVE
317 static
318 #endif
319 int OldvcV = 0;
320
321 void
322 Refresh(void)
323 {
324     int cur_line;
325     Char *cp;
326     int     cur_h, cur_v = 0, new_vcv;
327     int     rhdiff;
328     Char    oldgetting;
329
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 */
336
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;
343
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 */
349
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;
356         }
357         cp += Draw(cp, cp == InputBuf);
358     }
359
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;
363     }
364
365     rhdiff = TermH - vcursor_h - rprompt_h;
366     if (rprompt_h != 0 && rprompt_v == 0 && vcursor_v == 0 && rhdiff > 1) {
367                         /*
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.
371                          */
372         while (--rhdiff > 0)            /* pad out with spaces */
373             Vdraw(' ', 1);
374         RefreshPromptpart(RPrompt);
375     }
376     else {
377         rprompt_h = 0;                  /* flag "not using rprompt" */
378         rprompt_v = 0;
379     }
380
381     new_vcv = vcursor_v;        /* must be done BEFORE the NUL is written */
382     Vdraw('\0', 1);             /* put NUL on end */
383
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 */
388
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 */
398
399         /*
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.
404          */
405         cpy_pad_spaces(Display[cur_line], Vdisplay[cur_line], TermH);
406     }
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';
415         }
416     }
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 */
430 }
431
432 #ifdef notdef
433 GotoBottom(void)
434 {                               /* used to go to last used screen line */
435     MoveToLine(OldvcV);
436 }
437
438 #endif 
439
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();
448 }
449
450
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)
455 {
456     Char *a, *b;
457
458     if (num <= 0)
459         return;
460     if (num > dlen - dat)
461         num = dlen - dat;
462
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 */
468
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 */
476     }
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 */
482
483     /* copy the characters */
484     for (a = d + dat; (a < d + dlen) && (num > 0); num--)
485         *a++ = *s++;
486
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 */
492 }
493
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)
497 {
498     Char *a, *b;
499
500     if (num <= 0)
501         return;
502     if (dat + num >= dlen) {
503         d[dat] = '\0';
504         return;
505     }
506
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 */
511
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 */
519     }
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 */
524 }
525
526 static void
527 str_cp(Char *a, Char *b, int n)
528 {
529     while (n-- && *b)
530         *a++ = *b++;
531 }
532
533
534 /* ****************************************************************
535     update_line() is based on finding the middle difference of each line
536     on the screen; vis:
537
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
546
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 **************************************************************** */
550
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.
555  */
556 #define MIN_END_KEEP    4
557
558 static void                     /* could be changed to make it smarter */
559 update_line(Char *old, Char *new, int cur_line)
560 {
561     Char *o, *n, *p, c;
562     Char  *ofd, *ols, *oe, *nfd, *nls, *ne;
563     Char  *osb, *ose, *nsb, *nse;
564     int     fx, sx;
565
566     /*
567      * find first diff (won't be CHAR_DBWIDTH in either line)
568      */
569     for (o = old, n = new; *o && (*o == *n); o++, n++)
570         continue;
571     ofd = o;
572     nfd = n;
573
574     /*
575      * Find the end of both old and new
576      */
577     o = Strend(o);
578
579     /* 
580      * Remove any trailing blanks off of the end, being careful not to
581      * back up past the beginning.
582      */
583     if (!(adrof(STRhighlight) && MarkIsSet)) {
584     while (ofd < o) {
585         if (o[-1] != ' ')
586             break;
587         o--;
588     }
589     }
590     oe = o;
591     *oe = (Char) 0;
592
593     n = Strend(n);
594
595     /* remove blanks from end of new */
596     if (!(adrof(STRhighlight) && MarkIsSet)) {
597     while (nfd < n) {
598         if (n[-1] != ' ')
599             break;
600         n--;
601     }
602     }
603     ne = n;
604     *ne = (Char) 0;
605   
606     /*
607      * if no diff, continue to next line of redraw
608      */
609     if (*ofd == '\0' && *nfd == '\0') {
610 #ifdef DEBUG_UPDATE
611         dprintf("no difference.\r\n");
612 #endif /* DEBUG_UPDATE */
613         return;
614     }
615
616     /*
617      * find last same pointer
618      */
619     while ((o > ofd) && (n > nfd) && (*--o == *--n))
620         continue;
621     if (*o != *n) {
622         o++;
623         n++;
624     }
625     while (*o == CHAR_DBWIDTH) {
626         o++;
627         n++;
628     }
629     ols = o;
630     nls = n;
631
632     /*
633      * find same begining and same end
634      */
635     osb = ols;
636     nsb = nls;
637     ose = ols;
638     nse = nls;
639
640     /*
641      * case 1: insert: scan from nfd to nls looking for *ofd
642      */
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;
648                 /*
649                  * if the new match is longer and it's worth keeping, then we
650                  * take it
651                  */
652                 if (((nse - nsb) < (p - n)) && (2 * (p - n) > n - nfd)) {
653                     nsb = n;
654                     nse = p;
655                     osb = ofd;
656                     ose = o;
657                 }
658             }
659         }
660     }
661
662     /*
663      * case 2: delete: scan from ofd to ols looking for *nfd
664      */
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;
670                 /*
671                  * if the new match is longer and it's worth keeping, then we
672                  * take it
673                  */
674                 if (((ose - osb) < (p - o)) && (2 * (p - o) > o - ofd)) {
675                     nsb = nfd;
676                     nse = n;
677                     osb = o;
678                     ose = p;
679                 }
680             }
681         }
682     }
683 #ifdef notdef
684     /*
685      * If `last same' is before `same end' re-adjust
686      */
687     if (ols < ose)
688         ols = ose;
689     if (nls < nse)
690         nls = nse;
691 #endif
692
693     /*
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.
696      */
697     if ((oe - ols) < MIN_END_KEEP) {
698         ols = oe;
699         nls = ne;
700     }
701
702     /*
703      * Pragmatics II: if the terminal isn't smart enough, make the data dumber
704      * so the smart update doesn't try anything fancy
705      */
706
707     /*
708      * fx is the number of characters we need to insert/delete: in the
709      * beginning to bring the two same begins together
710      */
711     fx = (int) ((nsb - nfd) - (osb - ofd));
712     /*
713      * sx is the number of characters we need to insert/delete: in the end to
714      * bring the two same last parts together
715      */
716     sx = (int) ((nls - nse) - (ols - ose));
717
718     if (!T_CanIns) {
719         if (fx > 0) {
720             osb = ols;
721             ose = ols;
722             nsb = nls;
723             nse = nls;
724         }
725         if (sx > 0) {
726             ols = oe;
727             nls = ne;
728         }
729         if ((ols - ofd) < (nls - nfd)) {
730             ols = oe;
731             nls = ne;
732         }
733     }
734     if (!T_CanDel) {
735         if (fx < 0) {
736             osb = ols;
737             ose = ols;
738             nsb = nls;
739             nse = nls;
740         }
741         if (sx < 0) {
742             ols = oe;
743             nls = ne;
744         }
745         if ((ols - ofd) > (nls - nfd)) {
746             ols = oe;
747             nls = ne;
748         }
749     }
750
751     /*
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).
754      */
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;
762     }
763
764     /*
765      * Now that we are done with pragmatics we recompute fx, sx
766      */
767     fx = (int) ((nsb - nfd) - (osb - ofd));
768     sx = (int) ((nls - nse) - (ols - ose));
769
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 */
791
792     /*
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
796      */
797     MoveToLine(cur_line);
798
799     /*
800      * at this point we have something like this:
801      * 
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
808      * 
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).
813      *
814      * sx is the same for the second differences.
815      */
816
817     /*
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.
823      */
824
825     /*
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
829      */
830     p = (ols != oe) ? oe : ose;
831
832     /*
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
837      */
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 */
842         /*
843          * Move to the first char to insert, where the first diff is.
844          */
845         MoveToChar(nfd - new);
846         /*
847          * Check if we have stuff to keep at end
848          */
849         if (nsb != ne) {
850 #ifdef DEBUG_UPDATE
851             dprintf("with stuff to keep at end\r\n");
852 #endif  /* DEBUG_UPDATE */
853             /*
854              * insert fx chars of new starting at nfd
855              */
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);
863             }
864             /*
865              * write (nsb-nfd) - fx chars of new starting at (nfd + fx)
866              */
867             so_write(nfd + fx, (nsb - nfd) - fx);
868             str_cp(ofd + fx, nfd + fx, (int) ((nsb - nfd) - fx));
869         }
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));
876             /*
877              * Done
878              */
879             return;
880         }
881     }
882     else if (fx < 0) {
883 #ifdef DEBUG_UPDATE
884         dprintf("first diff delete at %d...\r\n", ofd - old);
885 #endif  /* DEBUG_UPDATE */
886         /*
887          * move to the first char to delete where the first diff is
888          */
889         MoveToChar(ofd - old);
890         /*
891          * Check if we have stuff to save
892          */
893         if (osb != oe) {
894 #ifdef DEBUG_UPDATE
895             dprintf("with stuff to save at end\r\n");
896 #endif  /* DEBUG_UPDATE */
897             /*
898              * fx is less than zero *always* here but we check for code
899              * symmetry
900              */
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);
908             }
909             /*
910              * write (nsb-nfd) chars of new starting at nfd
911              */
912             so_write(nfd, (nsb - nfd));
913             str_cp(ofd, nfd, (int) (nsb - nfd));
914
915         }
916         else {
917 #ifdef DEBUG_UPDATE
918             dprintf("but with nothing left to save\r\n");
919 #endif  /* DEBUG_UPDATE */
920             /*
921              * write (nsb-nfd) chars of new starting at nfd
922              */
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
930             /*
931              * The calculation above does not work too well on NT
932              */
933             ClearEOL(TermH - CursorH);
934 #endif /*WINNT_NATIVE*/
935             /*
936              * Done
937              */
938             return;
939         }
940     }
941     else
942         fx = 0;
943
944     if (sx < 0) {
945 #ifdef DEBUG_UPDATE
946         dprintf("second diff delete at %d...\r\n", (ose - old) + fx);
947 #endif  /* DEBUG_UPDATE */
948         /*
949          * Check if we have stuff to delete
950          */
951         /*
952          * fx is the number of characters inserted (+) or deleted (-)
953          */
954
955         MoveToChar((ose - old) + fx);
956         /*
957          * Check if we have stuff to save
958          */
959         if (ols != oe) {
960 #ifdef DEBUG_UPDATE
961             dprintf("with stuff to save at end\r\n");
962 #endif  /* DEBUG_UPDATE */
963             /*
964              * Again a duplicate test.
965              */
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);
972             }
973
974             /*
975              * write (nls-nse) chars of new starting at nse
976              */
977             so_write(nse, (nls - nse));
978         }
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
993             /*
994              * The calculation above does not work too well on NT
995              */
996             ClearEOL(TermH - CursorH);
997 #endif /*WINNT_NATIVE*/
998         }
999     }
1000
1001     /*
1002      * if we have a first insert AND WE HAVEN'T ALREADY DONE IT...
1003      */
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 */
1008
1009         MoveToChar(nfd - new);
1010         /*
1011          * Check if we have stuff to keep at the end
1012          */
1013         if (nsb != ne) {
1014 #ifdef DEBUG_UPDATE
1015             dprintf("with stuff to keep at end\r\n");
1016 #endif /* DEBUG_UPDATE */
1017             /* 
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.
1021              */
1022             fx = (int) ((nsb - nfd) - (osb - ofd));
1023             if (fx > 0) {
1024                 /*
1025                  * insert fx chars of new starting at nfd
1026                  */
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);
1033             }
1034
1035             /*
1036              * write (nsb-nfd) - fx chars of new starting at (nfd + fx)
1037              */
1038             so_write(nfd + fx, (nsb - nfd) - fx);
1039             str_cp(ofd + fx, nfd + fx, (int) ((nsb - nfd) - fx));
1040         }
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));
1047         }
1048     }
1049
1050     /*
1051      * line is now NEW up to nse
1052      */
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);
1069             }
1070
1071             /*
1072              * write (nls-nse) - sx chars of new starting at (nse + sx)
1073              */
1074             so_write(nse + sx, (nls - nse) - sx);
1075         }
1076         else {
1077 #ifdef DEBUG_UPDATE
1078             dprintf("without anything to save\r\n");
1079 #endif /* DEBUG_UPDATE */
1080             so_write(nse, (nls - nse));
1081
1082             /*
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.
1086              */
1087         }
1088     }
1089 #ifdef DEBUG_UPDATE
1090     dprintf("done.\r\n");
1091 #endif /* DEBUG_UPDATE */
1092 }
1093
1094
1095 static void
1096 cpy_pad_spaces(Char *dst, Char *src, int width)
1097 {
1098     int i;
1099
1100     for (i = 0; i < width; i++) {
1101         if (*src == (Char) 0)
1102             break;
1103         *dst++ = *src++;
1104     }
1105
1106     while (i < width) {
1107         *dst++ = ' ';
1108         i++;
1109     }
1110     *dst = (Char) 0;
1111 }
1112
1113 void
1114 RefCursor(void)
1115 {                               /* only move to new cursor pos */
1116     Char *cp;
1117     int w, h, th, v;
1118
1119     /* first we must find where the cursor is... */
1120     h = 0;
1121     v = 0;
1122     th = TermH;                 /* optimize for speed */
1123
1124     for (cp = Prompt; cp != NULL && *cp; ) {    /* do prompt */
1125         if (*cp & LITERAL) {
1126             cp++;
1127             continue;
1128         }
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)
1138                     ;
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;
1153         }
1154         if (h >= th) {          /* check, extra long tabs picked up here also */
1155             h -= th;
1156             v++;
1157         }
1158     }
1159
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)
1170                     ;
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;
1185         }
1186         if (h >= th) {          /* check, extra long tabs picked up here also */
1187             h -= th;
1188             v++;
1189         }
1190     }
1191
1192     /* now go there */
1193     MoveToLine(v);
1194     MoveToChar(h);
1195     if (adrof(STRhighlight) && MarkIsSet) {
1196         ClearLines();
1197         ClearDisp();
1198         Refresh();
1199     }
1200     flush();
1201 }
1202
1203 #ifndef WINTT_NATIVE
1204 static void
1205 PutPlusOne(Char c, int width)
1206 {
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);
1215     }
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');
1227             }
1228         }
1229         else {
1230             (void) putraw('\r');
1231             (void) putraw('\n');
1232         }
1233     }
1234 }
1235 #endif
1236
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;
1243
1244     if (Cursor != LastChar) {
1245         Refresh();              /* too hard to handle */
1246         return;
1247     }
1248     if (rprompt_h != 0 && (TermH - CursorH - rprompt_h < 3)) {
1249         Refresh();              /* clear out rprompt if less than one char gap*/
1250         return;
1251     }
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;
1261             }
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;
1288     }
1289     flush();
1290 }
1291
1292 /* clear the screen buffers so that new new prompt starts fresh. */
1293
1294 void
1295 ClearDisp(void)
1296 {
1297     int i;
1298
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;
1305 }
1306
1307 void
1308 ClearLines(void)
1309 {                               /* Make sure all lines are *really* blank */
1310     int i;
1311
1312     if (T_CanCEOL) {
1313         /*
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.
1318          */
1319         for (i = OldvcV; i >= 0; i--) { /* for each line on the screen */
1320             MoveToLine(i);
1321             MoveToChar(0);
1322             ClearEOL(TermH);
1323         }
1324     }
1325     else {
1326         MoveToLine(OldvcV);     /* go to last line */
1327         (void) putraw('\r');    /* go to BOL */
1328         (void) putraw('\n');    /* go to new line */
1329     }
1330 }