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