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