]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libedit/refresh.c
Update compiler-rt to 3.9.0 release, and update the build glue for
[FreeBSD/FreeBSD.git] / lib / libedit / refresh.c
1 /*      $NetBSD: refresh.c,v 1.37 2011/07/29 23:44:45 christos Exp $    */
2
3 /*-
4  * Copyright (c) 1992, 1993
5  *      The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Christos Zoulas of Cornell University.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34
35 #include "config.h"
36 #if !defined(lint) && !defined(SCCSID)
37 #if 0
38 static char sccsid[] = "@(#)refresh.c   8.1 (Berkeley) 6/4/93";
39 #else
40 __RCSID("$NetBSD: refresh.c,v 1.37 2011/07/29 23:44:45 christos Exp $");
41 #endif
42 #endif /* not lint && not SCCSID */
43 #include <sys/cdefs.h>
44 __FBSDID("$FreeBSD$");
45
46 /*
47  * refresh.c: Lower level screen refreshing functions
48  */
49 #include <stdio.h>
50 #include <ctype.h>
51 #include <unistd.h>
52 #include <string.h>
53
54 #include "el.h"
55
56 private void    re_nextline(EditLine *);
57 private void    re_addc(EditLine *, Int);
58 private void    re_update_line(EditLine *, Char *, Char *, int);
59 private void    re_insert (EditLine *, Char *, int, int, Char *, int);
60 private void    re_delete(EditLine *, Char *, int, int, int);
61 private void    re_fastputc(EditLine *, Int);
62 private void    re_clear_eol(EditLine *, int, int, int);
63 private void    re__strncopy(Char *, Char *, size_t);
64 private void    re__copy_and_pad(Char *, const Char *, size_t);
65
66 #ifdef DEBUG_REFRESH
67 private void    re_printstr(EditLine *, const char *, char *, char *);
68 #define __F el->el_errfile
69 #define ELRE_ASSERT(a, b, c)    do                              \
70                                     if (/*CONSTCOND*/ a) {      \
71                                         (void) fprintf b;       \
72                                         c;                      \
73                                     }                           \
74                                 while (/*CONSTCOND*/0)
75 #define ELRE_DEBUG(a, b)        ELRE_ASSERT(a,b,;)
76
77 /* re_printstr():
78  *      Print a string on the debugging pty
79  */
80 private void
81 re_printstr(EditLine *el, const char *str, char *f, char *t)
82 {
83
84         ELRE_DEBUG(1, (__F, "%s:\"", str));
85         while (f < t)
86                 ELRE_DEBUG(1, (__F, "%c", *f++ & 0177));
87         ELRE_DEBUG(1, (__F, "\"\r\n"));
88 }
89 #else
90 #define ELRE_ASSERT(a, b, c)
91 #define ELRE_DEBUG(a, b)
92 #endif
93
94 /* re_nextline():
95  *      Move to the next line or scroll
96  */
97 private void
98 re_nextline(EditLine *el)
99 {
100         el->el_refresh.r_cursor.h = 0;  /* reset it. */
101
102         /*
103          * If we would overflow (input is longer than terminal size),
104          * emulate scroll by dropping first line and shuffling the rest.
105          * We do this via pointer shuffling - it's safe in this case
106          * and we avoid memcpy().
107          */
108         if (el->el_refresh.r_cursor.v + 1 >= el->el_terminal.t_size.v) {
109                 int i, lins = el->el_terminal.t_size.v;
110                 Char *firstline = el->el_vdisplay[0];
111
112                 for(i = 1; i < lins; i++)
113                         el->el_vdisplay[i - 1] = el->el_vdisplay[i];
114
115                 firstline[0] = '\0';            /* empty the string */  
116                 el->el_vdisplay[i - 1] = firstline;
117         } else
118                 el->el_refresh.r_cursor.v++;
119
120         ELRE_ASSERT(el->el_refresh.r_cursor.v >= el->el_terminal.t_size.v,
121             (__F, "\r\nre_putc: overflow! r_cursor.v == %d > %d\r\n",
122             el->el_refresh.r_cursor.v, el->el_terminal.t_size.v),
123             abort());
124 }
125
126 /* re_addc():
127  *      Draw c, expanding tabs, control chars etc.
128  */
129 private void
130 re_addc(EditLine *el, Int c)
131 {
132         switch (ct_chr_class((Char)c)) {
133         case CHTYPE_TAB:        /* expand the tab */
134                 for (;;) {
135                         re_putc(el, ' ', 1);
136                         if ((el->el_refresh.r_cursor.h & 07) == 0)
137                                 break;                  /* go until tab stop */
138                 }
139                 break;
140         case CHTYPE_NL: {
141                 int oldv = el->el_refresh.r_cursor.v;
142                 re_putc(el, '\0', 0);                   /* assure end of line */
143                 if (oldv == el->el_refresh.r_cursor.v)  /* XXX */
144                         re_nextline(el);
145                 break;
146         }
147         case CHTYPE_PRINT:
148                 re_putc(el, c, 1);
149                 break;
150         default: {
151                 Char visbuf[VISUAL_WIDTH_MAX];
152                 ssize_t i, n =
153                     ct_visual_char(visbuf, VISUAL_WIDTH_MAX, (Char)c);
154                 for (i = 0; n-- > 0; ++i)
155                     re_putc(el, visbuf[i], 1);
156                 break;
157         }
158         }
159 }
160
161
162 /* re_putc():
163  *      Draw the character given
164  */
165 protected void
166 re_putc(EditLine *el, Int c, int shift)
167 {
168         int i, w = Width(c);
169         ELRE_DEBUG(1, (__F, "printing %5x '%c'\r\n", c, c));
170
171         while (shift && (el->el_refresh.r_cursor.h + w > el->el_terminal.t_size.h))
172             re_putc(el, ' ', 1);
173
174         el->el_vdisplay[el->el_refresh.r_cursor.v]
175             [el->el_refresh.r_cursor.h] = c;
176         /* assumes !shift is only used for single-column chars */
177         i = w;
178         while (--i > 0)
179                 el->el_vdisplay[el->el_refresh.r_cursor.v]
180                     [el->el_refresh.r_cursor.h + i] = MB_FILL_CHAR;
181
182         if (!shift)
183                 return;
184
185         el->el_refresh.r_cursor.h += w; /* advance to next place */
186         if (el->el_refresh.r_cursor.h >= el->el_terminal.t_size.h) {
187                 /* assure end of line */
188                 el->el_vdisplay[el->el_refresh.r_cursor.v][el->el_terminal.t_size.h]
189                     = '\0';
190                 re_nextline(el);
191         }
192 }
193
194
195 /* re_refresh():
196  *      draws the new virtual screen image from the current input
197  *      line, then goes line-by-line changing the real image to the new
198  *      virtual image. The routine to re-draw a line can be replaced
199  *      easily in hopes of a smarter one being placed there.
200  */
201 protected void
202 re_refresh(EditLine *el)
203 {
204         int i, rhdiff;
205         Char *cp, *st;
206         coord_t cur;
207 #ifdef notyet
208         size_t termsz;
209 #endif
210
211         ELRE_DEBUG(1, (__F, "el->el_line.buffer = :%s:\r\n",
212             el->el_line.buffer));
213
214         /* reset the Drawing cursor */
215         el->el_refresh.r_cursor.h = 0;
216         el->el_refresh.r_cursor.v = 0;
217
218         /* temporarily draw rprompt to calculate its size */
219         prompt_print(el, EL_RPROMPT);
220
221         /* reset the Drawing cursor */
222         el->el_refresh.r_cursor.h = 0;
223         el->el_refresh.r_cursor.v = 0;
224
225         if (el->el_line.cursor >= el->el_line.lastchar) {
226                 if (el->el_map.current == el->el_map.alt
227                     && el->el_line.lastchar != el->el_line.buffer)
228                         el->el_line.cursor = el->el_line.lastchar - 1;
229                 else
230                         el->el_line.cursor = el->el_line.lastchar;
231         }
232
233         cur.h = -1;             /* set flag in case I'm not set */
234         cur.v = 0;
235
236         prompt_print(el, EL_PROMPT);
237
238         /* draw the current input buffer */
239 #if notyet
240         termsz = el->el_terminal.t_size.h * el->el_terminal.t_size.v;
241         if (el->el_line.lastchar - el->el_line.buffer > termsz) {
242                 /*
243                  * If line is longer than terminal, process only part
244                  * of line which would influence display.
245                  */
246                 size_t rem = (el->el_line.lastchar-el->el_line.buffer)%termsz;
247
248                 st = el->el_line.lastchar - rem
249                         - (termsz - (((rem / el->el_terminal.t_size.v) - 1)
250                                         * el->el_terminal.t_size.v));
251         } else
252 #endif
253                 st = el->el_line.buffer;
254
255         for (cp = st; cp < el->el_line.lastchar; cp++) {
256                 if (cp == el->el_line.cursor) {
257                         int w = Width(*cp);
258                         /* save for later */
259                         cur.h = el->el_refresh.r_cursor.h;
260                         cur.v = el->el_refresh.r_cursor.v;
261                         /* handle being at a linebroken doublewidth char */
262                         if (w > 1 && el->el_refresh.r_cursor.h + w >
263                             el->el_terminal.t_size.h) {
264                                 cur.h = 0;
265                                 cur.v++;
266                         }
267                 }
268                 re_addc(el, *cp);
269         }
270
271         if (cur.h == -1) {      /* if I haven't been set yet, I'm at the end */
272                 cur.h = el->el_refresh.r_cursor.h;
273                 cur.v = el->el_refresh.r_cursor.v;
274         }
275         rhdiff = el->el_terminal.t_size.h - el->el_refresh.r_cursor.h -
276             el->el_rprompt.p_pos.h;
277         if (el->el_rprompt.p_pos.h && !el->el_rprompt.p_pos.v &&
278             !el->el_refresh.r_cursor.v && rhdiff > 1) {
279                 /*
280                  * have a right-hand side prompt that will fit
281                  * on the end of the first line with at least
282                  * one character gap to the input buffer.
283                  */
284                 while (--rhdiff > 0)    /* pad out with spaces */
285                         re_putc(el, ' ', 1);
286                 prompt_print(el, EL_RPROMPT);
287         } else {
288                 el->el_rprompt.p_pos.h = 0;     /* flag "not using rprompt" */
289                 el->el_rprompt.p_pos.v = 0;
290         }
291
292         re_putc(el, '\0', 0);   /* make line ended with NUL, no cursor shift */
293
294         el->el_refresh.r_newcv = el->el_refresh.r_cursor.v;
295
296         ELRE_DEBUG(1, (__F,
297                 "term.h=%d vcur.h=%d vcur.v=%d vdisplay[0]=\r\n:%80.80s:\r\n",
298                 el->el_terminal.t_size.h, el->el_refresh.r_cursor.h,
299                 el->el_refresh.r_cursor.v, ct_encode_string(el->el_vdisplay[0])));
300
301         ELRE_DEBUG(1, (__F, "updating %d lines.\r\n", el->el_refresh.r_newcv));
302         for (i = 0; i <= el->el_refresh.r_newcv; i++) {
303                 /* NOTE THAT re_update_line MAY CHANGE el_display[i] */
304                 re_update_line(el, el->el_display[i], el->el_vdisplay[i], i);
305
306                 /*
307                  * Copy the new line to be the current one, and pad out with
308                  * spaces to the full width of the terminal so that if we try
309                  * moving the cursor by writing the character that is at the
310                  * end of the screen line, it won't be a NUL or some old
311                  * leftover stuff.
312                  */
313                 re__copy_and_pad(el->el_display[i], el->el_vdisplay[i],
314                     (size_t) el->el_terminal.t_size.h);
315         }
316         ELRE_DEBUG(1, (__F,
317         "\r\nel->el_refresh.r_cursor.v=%d,el->el_refresh.r_oldcv=%d i=%d\r\n",
318             el->el_refresh.r_cursor.v, el->el_refresh.r_oldcv, i));
319
320         if (el->el_refresh.r_oldcv > el->el_refresh.r_newcv)
321                 for (; i <= el->el_refresh.r_oldcv; i++) {
322                         terminal_move_to_line(el, i);
323                         terminal_move_to_char(el, 0);
324                         /* This Strlen should be safe even with MB_FILL_CHARs */
325                         terminal_clear_EOL(el, (int) Strlen(el->el_display[i]));
326 #ifdef DEBUG_REFRESH
327                         terminal_overwrite(el, "C\b", (size_t)2);
328 #endif /* DEBUG_REFRESH */
329                         el->el_display[i][0] = '\0';
330                 }
331
332         el->el_refresh.r_oldcv = el->el_refresh.r_newcv; /* set for next time */
333         ELRE_DEBUG(1, (__F,
334             "\r\ncursor.h = %d, cursor.v = %d, cur.h = %d, cur.v = %d\r\n",
335             el->el_refresh.r_cursor.h, el->el_refresh.r_cursor.v,
336             cur.h, cur.v));
337         terminal_move_to_line(el, cur.v);       /* go to where the cursor is */
338         terminal_move_to_char(el, cur.h);
339 }
340
341
342 /* re_goto_bottom():
343  *       used to go to last used screen line
344  */
345 protected void
346 re_goto_bottom(EditLine *el)
347 {
348
349         terminal_move_to_line(el, el->el_refresh.r_oldcv);
350         terminal__putc(el, '\n');
351         re_clear_display(el);
352         terminal__flush(el);
353 }
354
355
356 /* re_insert():
357  *      insert num characters of s into d (in front of the character)
358  *      at dat, maximum length of d is dlen
359  */
360 private void
361 /*ARGSUSED*/
362 re_insert(EditLine *el __attribute__((__unused__)),
363     Char *d, int dat, int dlen, Char *s, int num)
364 {
365         Char *a, *b;
366
367         if (num <= 0)
368                 return;
369         if (num > dlen - dat)
370                 num = dlen - dat;
371
372         ELRE_DEBUG(1,
373             (__F, "re_insert() starting: %d at %d max %d, d == \"%s\"\n",
374             num, dat, dlen, ct_encode_string(d)));
375         ELRE_DEBUG(1, (__F, "s == \"%s\"\n", ct_encode_string(s)));
376
377         /* open up the space for num chars */
378         if (num > 0) {
379                 b = d + dlen - 1;
380                 a = b - num;
381                 while (a >= &d[dat])
382                         *b-- = *a--;
383                 d[dlen] = '\0'; /* just in case */
384         }
385
386         ELRE_DEBUG(1, (__F,
387                 "re_insert() after insert: %d at %d max %d, d == \"%s\"\n",
388                 num, dat, dlen, ct_encode_string(d)));
389         ELRE_DEBUG(1, (__F, "s == \"%s\"\n", ct_encode_string(s)));
390
391         /* copy the characters */
392         for (a = d + dat; (a < d + dlen) && (num > 0); num--)
393                 *a++ = *s++;
394
395 #ifdef notyet
396         /* ct_encode_string() uses a static buffer, so we can't conveniently
397          * encode both d & s here */
398         ELRE_DEBUG(1,
399             (__F, "re_insert() after copy: %d at %d max %d, %s == \"%s\"\n",
400             num, dat, dlen, d, s));
401         ELRE_DEBUG(1, (__F, "s == \"%s\"\n", s));
402 #endif
403 }
404
405
406 /* re_delete():
407  *      delete num characters d at dat, maximum length of d is dlen
408  */
409 private void
410 /*ARGSUSED*/
411 re_delete(EditLine *el __attribute__((__unused__)),
412     Char *d, int dat, int dlen, int num)
413 {
414         Char *a, *b;
415
416         if (num <= 0)
417                 return;
418         if (dat + num >= dlen) {
419                 d[dat] = '\0';
420                 return;
421         }
422         ELRE_DEBUG(1,
423             (__F, "re_delete() starting: %d at %d max %d, d == \"%s\"\n",
424             num, dat, dlen, ct_encode_string(d)));
425
426         /* open up the space for num chars */
427         if (num > 0) {
428                 b = d + dat;
429                 a = b + num;
430                 while (a < &d[dlen])
431                         *b++ = *a++;
432                 d[dlen] = '\0'; /* just in case */
433         }
434         ELRE_DEBUG(1,
435             (__F, "re_delete() after delete: %d at %d max %d, d == \"%s\"\n",
436             num, dat, dlen, ct_encode_string(d)));
437 }
438
439
440 /* re__strncopy():
441  *      Like strncpy without padding.
442  */
443 private void
444 re__strncopy(Char *a, Char *b, size_t n)
445 {
446
447         while (n-- && *b)
448                 *a++ = *b++;
449 }
450
451 /* re_clear_eol():
452  *      Find the number of characters we need to clear till the end of line
453  *      in order to make sure that we have cleared the previous contents of
454  *      the line. fx and sx is the number of characters inserted or deleted
455  *      in the first or second diff, diff is the difference between the
456  *      number of characters between the new and old line.
457  */
458 private void
459 re_clear_eol(EditLine *el, int fx, int sx, int diff)
460 {
461
462         ELRE_DEBUG(1, (__F, "re_clear_eol sx %d, fx %d, diff %d\n",
463             sx, fx, diff));
464
465         if (fx < 0)
466                 fx = -fx;
467         if (sx < 0)
468                 sx = -sx;
469         if (fx > diff)
470                 diff = fx;
471         if (sx > diff)
472                 diff = sx;
473
474         ELRE_DEBUG(1, (__F, "re_clear_eol %d\n", diff));
475         terminal_clear_EOL(el, diff);
476 }
477
478 /*****************************************************************
479     re_update_line() is based on finding the middle difference of each line
480     on the screen; vis:
481
482                              /old first difference
483         /beginning of line   |              /old last same       /old EOL
484         v                    v              v                    v
485 old:    eddie> Oh, my little gruntle-buggy is to me, as lurgid as
486 new:    eddie> Oh, my little buggy says to me, as lurgid as
487         ^                    ^        ^                    ^
488         \beginning of line   |        \new last same       \new end of line
489                              \new first difference
490
491     all are character pointers for the sake of speed.  Special cases for
492     no differences, as well as for end of line additions must be handled.
493 **************************************************************** */
494
495 /* Minimum at which doing an insert it "worth it".  This should be about
496  * half the "cost" of going into insert mode, inserting a character, and
497  * going back out.  This should really be calculated from the termcap
498  * data...  For the moment, a good number for ANSI terminals.
499  */
500 #define MIN_END_KEEP    4
501
502 private void
503 re_update_line(EditLine *el, Char *old, Char *new, int i)
504 {
505         Char *o, *n, *p, c;
506         Char *ofd, *ols, *oe, *nfd, *nls, *ne;
507         Char *osb, *ose, *nsb, *nse;
508         int fx, sx;
509         size_t len;
510
511         /*
512          * find first diff
513          */
514         for (o = old, n = new; *o && (*o == *n); o++, n++)
515                 continue;
516         ofd = o;
517         nfd = n;
518
519         /*
520          * Find the end of both old and new
521          */
522         while (*o)
523                 o++;
524         /*
525          * Remove any trailing blanks off of the end, being careful not to
526          * back up past the beginning.
527          */
528         while (ofd < o) {
529                 if (o[-1] != ' ')
530                         break;
531                 o--;
532         }
533         oe = o;
534         *oe = '\0';
535
536         while (*n)
537                 n++;
538
539         /* remove blanks from end of new */
540         while (nfd < n) {
541                 if (n[-1] != ' ')
542                         break;
543                 n--;
544         }
545         ne = n;
546         *ne = '\0';
547
548         /*
549          * if no diff, continue to next line of redraw
550          */
551         if (*ofd == '\0' && *nfd == '\0') {
552                 ELRE_DEBUG(1, (__F, "no difference.\r\n"));
553                 return;
554         }
555         /*
556          * find last same pointer
557          */
558         while ((o > ofd) && (n > nfd) && (*--o == *--n))
559                 continue;
560         ols = ++o;
561         nls = ++n;
562
563         /*
564          * find same beginning and same end
565          */
566         osb = ols;
567         nsb = nls;
568         ose = ols;
569         nse = nls;
570
571         /*
572          * case 1: insert: scan from nfd to nls looking for *ofd
573          */
574         if (*ofd) {
575                 for (c = *ofd, n = nfd; n < nls; n++) {
576                         if (c == *n) {
577                                 for (o = ofd, p = n;
578                                     p < nls && o < ols && *o == *p;
579                                     o++, p++)
580                                         continue;
581                                 /*
582                                  * if the new match is longer and it's worth
583                                  * keeping, then we take it
584                                  */
585                                 if (((nse - nsb) < (p - n)) &&
586                                     (2 * (p - n) > n - nfd)) {
587                                         nsb = n;
588                                         nse = p;
589                                         osb = ofd;
590                                         ose = o;
591                                 }
592                         }
593                 }
594         }
595         /*
596          * case 2: delete: scan from ofd to ols looking for *nfd
597          */
598         if (*nfd) {
599                 for (c = *nfd, o = ofd; o < ols; o++) {
600                         if (c == *o) {
601                                 for (n = nfd, p = o;
602                                     p < ols && n < nls && *p == *n;
603                                     p++, n++)
604                                         continue;
605                                 /*
606                                  * if the new match is longer and it's worth
607                                  * keeping, then we take it
608                                  */
609                                 if (((ose - osb) < (p - o)) &&
610                                     (2 * (p - o) > o - ofd)) {
611                                         nsb = nfd;
612                                         nse = n;
613                                         osb = o;
614                                         ose = p;
615                                 }
616                         }
617                 }
618         }
619         /*
620          * Pragmatics I: If old trailing whitespace or not enough characters to
621          * save to be worth it, then don't save the last same info.
622          */
623         if ((oe - ols) < MIN_END_KEEP) {
624                 ols = oe;
625                 nls = ne;
626         }
627         /*
628          * Pragmatics II: if the terminal isn't smart enough, make the data
629          * dumber so the smart update doesn't try anything fancy
630          */
631
632         /*
633          * fx is the number of characters we need to insert/delete: in the
634          * beginning to bring the two same begins together
635          */
636         fx = (int)((nsb - nfd) - (osb - ofd));
637         /*
638          * sx is the number of characters we need to insert/delete: in the
639          * end to bring the two same last parts together
640          */
641         sx = (int)((nls - nse) - (ols - ose));
642
643         if (!EL_CAN_INSERT) {
644                 if (fx > 0) {
645                         osb = ols;
646                         ose = ols;
647                         nsb = nls;
648                         nse = nls;
649                 }
650                 if (sx > 0) {
651                         ols = oe;
652                         nls = ne;
653                 }
654                 if ((ols - ofd) < (nls - nfd)) {
655                         ols = oe;
656                         nls = ne;
657                 }
658         }
659         if (!EL_CAN_DELETE) {
660                 if (fx < 0) {
661                         osb = ols;
662                         ose = ols;
663                         nsb = nls;
664                         nse = nls;
665                 }
666                 if (sx < 0) {
667                         ols = oe;
668                         nls = ne;
669                 }
670                 if ((ols - ofd) > (nls - nfd)) {
671                         ols = oe;
672                         nls = ne;
673                 }
674         }
675         /*
676          * Pragmatics III: make sure the middle shifted pointers are correct if
677          * they don't point to anything (we may have moved ols or nls).
678          */
679         /* if the change isn't worth it, don't bother */
680         /* was: if (osb == ose) */
681         if ((ose - osb) < MIN_END_KEEP) {
682                 osb = ols;
683                 ose = ols;
684                 nsb = nls;
685                 nse = nls;
686         }
687         /*
688          * Now that we are done with pragmatics we recompute fx, sx
689          */
690         fx = (int)((nsb - nfd) - (osb - ofd));
691         sx = (int)((nls - nse) - (ols - ose));
692
693         ELRE_DEBUG(1, (__F, "fx %d, sx %d\n", fx, sx));
694         ELRE_DEBUG(1, (__F, "ofd %d, osb %d, ose %d, ols %d, oe %d\n",
695                 ofd - old, osb - old, ose - old, ols - old, oe - old));
696         ELRE_DEBUG(1, (__F, "nfd %d, nsb %d, nse %d, nls %d, ne %d\n",
697                 nfd - new, nsb - new, nse - new, nls - new, ne - new));
698         ELRE_DEBUG(1, (__F,
699                 "xxx-xxx:\"00000000001111111111222222222233333333334\"\r\n"));
700         ELRE_DEBUG(1, (__F,
701                 "xxx-xxx:\"01234567890123456789012345678901234567890\"\r\n"));
702 #ifdef DEBUG_REFRESH
703         re_printstr(el, "old- oe", old, oe);
704         re_printstr(el, "new- ne", new, ne);
705         re_printstr(el, "old-ofd", old, ofd);
706         re_printstr(el, "new-nfd", new, nfd);
707         re_printstr(el, "ofd-osb", ofd, osb);
708         re_printstr(el, "nfd-nsb", nfd, nsb);
709         re_printstr(el, "osb-ose", osb, ose);
710         re_printstr(el, "nsb-nse", nsb, nse);
711         re_printstr(el, "ose-ols", ose, ols);
712         re_printstr(el, "nse-nls", nse, nls);
713         re_printstr(el, "ols- oe", ols, oe);
714         re_printstr(el, "nls- ne", nls, ne);
715 #endif /* DEBUG_REFRESH */
716
717         /*
718          * el_cursor.v to this line i MUST be in this routine so that if we
719          * don't have to change the line, we don't move to it. el_cursor.h to
720          * first diff char
721          */
722         terminal_move_to_line(el, i);
723
724         /*
725          * at this point we have something like this:
726          *
727          * /old                  /ofd    /osb               /ose    /ols     /oe
728          * v.....................v       v..................v       v........v
729          * eddie> Oh, my fredded gruntle-buggy is to me, as foo var lurgid as
730          * eddie> Oh, my fredded quiux buggy is to me, as gruntle-lurgid as
731          * ^.....................^     ^..................^       ^........^
732          * \new                  \nfd  \nsb               \nse     \nls    \ne
733          *
734          * fx is the difference in length between the chars between nfd and
735          * nsb, and the chars between ofd and osb, and is thus the number of
736          * characters to delete if < 0 (new is shorter than old, as above),
737          * or insert (new is longer than short).
738          *
739          * sx is the same for the second differences.
740          */
741
742         /*
743          * if we have a net insert on the first difference, AND inserting the
744          * net amount ((nsb-nfd) - (osb-ofd)) won't push the last useful
745          * character (which is ne if nls != ne, otherwise is nse) off the edge
746          * of the screen (el->el_terminal.t_size.h) else we do the deletes first
747          * so that we keep everything we need to.
748          */
749
750         /*
751          * if the last same is the same like the end, there is no last same
752          * part, otherwise we want to keep the last same part set p to the
753          * last useful old character
754          */
755         p = (ols != oe) ? oe : ose;
756
757         /*
758          * if (There is a diffence in the beginning) && (we need to insert
759          *   characters) && (the number of characters to insert is less than
760          *   the term width)
761          *      We need to do an insert!
762          * else if (we need to delete characters)
763          *      We need to delete characters!
764          * else
765          *      No insert or delete
766          */
767         if ((nsb != nfd) && fx > 0 &&
768             ((p - old) + fx <= el->el_terminal.t_size.h)) {
769                 ELRE_DEBUG(1,
770                     (__F, "first diff insert at %d...\r\n", nfd - new));
771                 /*
772                  * Move to the first char to insert, where the first diff is.
773                  */
774                 terminal_move_to_char(el, (int)(nfd - new));
775                 /*
776                  * Check if we have stuff to keep at end
777                  */
778                 if (nsb != ne) {
779                         ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
780                         /*
781                          * insert fx chars of new starting at nfd
782                          */
783                         if (fx > 0) {
784                                 ELRE_DEBUG(!EL_CAN_INSERT, (__F,
785                                 "ERROR: cannot insert in early first diff\n"));
786                                 terminal_insertwrite(el, nfd, fx);
787                                 re_insert(el, old, (int)(ofd - old),
788                                     el->el_terminal.t_size.h, nfd, fx);
789                         }
790                         /*
791                          * write (nsb-nfd) - fx chars of new starting at
792                          * (nfd + fx)
793                          */
794                         len = (size_t) ((nsb - nfd) - fx);
795                         terminal_overwrite(el, (nfd + fx), len);
796                         re__strncopy(ofd + fx, nfd + fx, len);
797                 } else {
798                         ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
799                         len = (size_t)(nsb - nfd);
800                         terminal_overwrite(el, nfd, len);
801                         re__strncopy(ofd, nfd, len);
802                         /*
803                          * Done
804                          */
805                         return;
806                 }
807         } else if (fx < 0) {
808                 ELRE_DEBUG(1,
809                     (__F, "first diff delete at %d...\r\n", ofd - old));
810                 /*
811                  * move to the first char to delete where the first diff is
812                  */
813                 terminal_move_to_char(el, (int)(ofd - old));
814                 /*
815                  * Check if we have stuff to save
816                  */
817                 if (osb != oe) {
818                         ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n"));
819                         /*
820                          * fx is less than zero *always* here but we check
821                          * for code symmetry
822                          */
823                         if (fx < 0) {
824                                 ELRE_DEBUG(!EL_CAN_DELETE, (__F,
825                                     "ERROR: cannot delete in first diff\n"));
826                                 terminal_deletechars(el, -fx);
827                                 re_delete(el, old, (int)(ofd - old),
828                                     el->el_terminal.t_size.h, -fx);
829                         }
830                         /*
831                          * write (nsb-nfd) chars of new starting at nfd
832                          */
833                         len = (size_t) (nsb - nfd);
834                         terminal_overwrite(el, nfd, len);
835                         re__strncopy(ofd, nfd, len);
836
837                 } else {
838                         ELRE_DEBUG(1, (__F,
839                             "but with nothing left to save\r\n"));
840                         /*
841                          * write (nsb-nfd) chars of new starting at nfd
842                          */
843                         terminal_overwrite(el, nfd, (size_t)(nsb - nfd));
844                         re_clear_eol(el, fx, sx,
845                             (int)((oe - old) - (ne - new)));
846                         /*
847                          * Done
848                          */
849                         return;
850                 }
851         } else
852                 fx = 0;
853
854         if (sx < 0 && (ose - old) + fx < el->el_terminal.t_size.h) {
855                 ELRE_DEBUG(1, (__F,
856                     "second diff delete at %d...\r\n", (ose - old) + fx));
857                 /*
858                  * Check if we have stuff to delete
859                  */
860                 /*
861                  * fx is the number of characters inserted (+) or deleted (-)
862                  */
863
864                 terminal_move_to_char(el, (int)((ose - old) + fx));
865                 /*
866                  * Check if we have stuff to save
867                  */
868                 if (ols != oe) {
869                         ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n"));
870                         /*
871                          * Again a duplicate test.
872                          */
873                         if (sx < 0) {
874                                 ELRE_DEBUG(!EL_CAN_DELETE, (__F,
875                                     "ERROR: cannot delete in second diff\n"));
876                                 terminal_deletechars(el, -sx);
877                         }
878                         /*
879                          * write (nls-nse) chars of new starting at nse
880                          */
881                         terminal_overwrite(el, nse, (size_t)(nls - nse));
882                 } else {
883                         ELRE_DEBUG(1, (__F,
884                             "but with nothing left to save\r\n"));
885                         terminal_overwrite(el, nse, (size_t)(nls - nse));
886                         re_clear_eol(el, fx, sx,
887                             (int)((oe - old) - (ne - new)));
888                 }
889         }
890         /*
891          * if we have a first insert AND WE HAVEN'T ALREADY DONE IT...
892          */
893         if ((nsb != nfd) && (osb - ofd) <= (nsb - nfd) && (fx == 0)) {
894                 ELRE_DEBUG(1, (__F, "late first diff insert at %d...\r\n",
895                     nfd - new));
896
897                 terminal_move_to_char(el, (int)(nfd - new));
898                 /*
899                  * Check if we have stuff to keep at the end
900                  */
901                 if (nsb != ne) {
902                         ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
903                         /*
904                          * We have to recalculate fx here because we set it
905                          * to zero above as a flag saying that we hadn't done
906                          * an early first insert.
907                          */
908                         fx = (int)((nsb - nfd) - (osb - ofd));
909                         if (fx > 0) {
910                                 /*
911                                  * insert fx chars of new starting at nfd
912                                  */
913                                 ELRE_DEBUG(!EL_CAN_INSERT, (__F,
914                                  "ERROR: cannot insert in late first diff\n"));
915                                 terminal_insertwrite(el, nfd, fx);
916                                 re_insert(el, old, (int)(ofd - old),
917                                     el->el_terminal.t_size.h, nfd, fx);
918                         }
919                         /*
920                          * write (nsb-nfd) - fx chars of new starting at
921                          * (nfd + fx)
922                          */
923                         len = (size_t) ((nsb - nfd) - fx);
924                         terminal_overwrite(el, (nfd + fx), len);
925                         re__strncopy(ofd + fx, nfd + fx, len);
926                 } else {
927                         ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
928                         len = (size_t) (nsb - nfd);
929                         terminal_overwrite(el, nfd, len);
930                         re__strncopy(ofd, nfd, len);
931                 }
932         }
933         /*
934          * line is now NEW up to nse
935          */
936         if (sx >= 0) {
937                 ELRE_DEBUG(1, (__F,
938                     "second diff insert at %d...\r\n", (int)(nse - new)));
939                 terminal_move_to_char(el, (int)(nse - new));
940                 if (ols != oe) {
941                         ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
942                         if (sx > 0) {
943                                 /* insert sx chars of new starting at nse */
944                                 ELRE_DEBUG(!EL_CAN_INSERT, (__F,
945                                     "ERROR: cannot insert in second diff\n"));
946                                 terminal_insertwrite(el, nse, sx);
947                         }
948                         /*
949                          * write (nls-nse) - sx chars of new starting at
950                          * (nse + sx)
951                          */
952                         terminal_overwrite(el, (nse + sx),
953                             (size_t)((nls - nse) - sx));
954                 } else {
955                         ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
956                         terminal_overwrite(el, nse, (size_t)(nls - nse));
957
958                         /*
959                          * No need to do a clear-to-end here because we were
960                          * doing a second insert, so we will have over
961                          * written all of the old string.
962                          */
963                 }
964         }
965         ELRE_DEBUG(1, (__F, "done.\r\n"));
966 }
967
968
969 /* re__copy_and_pad():
970  *      Copy string and pad with spaces
971  */
972 private void
973 re__copy_and_pad(Char *dst, const Char *src, size_t width)
974 {
975         size_t i;
976
977         for (i = 0; i < width; i++) {
978                 if (*src == '\0')
979                         break;
980                 *dst++ = *src++;
981         }
982
983         for (; i < width; i++)
984                 *dst++ = ' ';
985
986         *dst = '\0';
987 }
988
989
990 /* re_refresh_cursor():
991  *      Move to the new cursor position
992  */
993 protected void
994 re_refresh_cursor(EditLine *el)
995 {
996         Char *cp;
997         int h, v, th, w;
998
999         if (el->el_line.cursor >= el->el_line.lastchar) {
1000                 if (el->el_map.current == el->el_map.alt
1001                     && el->el_line.lastchar != el->el_line.buffer)
1002                         el->el_line.cursor = el->el_line.lastchar - 1;
1003                 else
1004                         el->el_line.cursor = el->el_line.lastchar;
1005         }
1006
1007         /* first we must find where the cursor is... */
1008         h = el->el_prompt.p_pos.h;
1009         v = el->el_prompt.p_pos.v;
1010         th = el->el_terminal.t_size.h;  /* optimize for speed */
1011
1012         /* do input buffer to el->el_line.cursor */
1013         for (cp = el->el_line.buffer; cp < el->el_line.cursor; cp++) {
1014                 switch (ct_chr_class(*cp)) {
1015                 case CHTYPE_NL:  /* handle newline in data part too */
1016                         h = 0;
1017                         v++;
1018                         break;
1019                 case CHTYPE_TAB: /* if a tab, to next tab stop */
1020                         while (++h & 07)
1021                                 continue;
1022                         break;
1023                 default:
1024                         w = Width(*cp);
1025                         if (w > 1 && h + w > th) { /* won't fit on line */
1026                                 h = 0;
1027                                 v++;
1028                         }
1029                         h += ct_visual_width(*cp);
1030                         break;
1031                 }
1032
1033                 if (h >= th) {  /* check, extra long tabs picked up here also */
1034                         h -= th;
1035                         v++;
1036                 }
1037         }
1038         /* if we have a next character, and it's a doublewidth one, we need to
1039          * check whether we need to linebreak for it to fit */
1040         if (cp < el->el_line.lastchar && (w = Width(*cp)) > 1)
1041                 if (h + w > th) {
1042                     h = 0;
1043                     v++;
1044                 }
1045
1046         /* now go there */
1047         terminal_move_to_line(el, v);
1048         terminal_move_to_char(el, h);
1049         terminal__flush(el);
1050 }
1051
1052
1053 /* re_fastputc():
1054  *      Add a character fast.
1055  */
1056 private void
1057 re_fastputc(EditLine *el, Int c)
1058 {
1059         int w = Width((Char)c);
1060         while (w > 1 && el->el_cursor.h + w > el->el_terminal.t_size.h)
1061             re_fastputc(el, ' ');
1062
1063         terminal__putc(el, c);
1064         el->el_display[el->el_cursor.v][el->el_cursor.h++] = c;
1065         while (--w > 0)
1066                 el->el_display[el->el_cursor.v][el->el_cursor.h++]
1067                         = MB_FILL_CHAR;
1068
1069         if (el->el_cursor.h >= el->el_terminal.t_size.h) {
1070                 /* if we must overflow */
1071                 el->el_cursor.h = 0;
1072
1073                 /*
1074                  * If we would overflow (input is longer than terminal size),
1075                  * emulate scroll by dropping first line and shuffling the rest.
1076                  * We do this via pointer shuffling - it's safe in this case
1077                  * and we avoid memcpy().
1078                  */
1079                 if (el->el_cursor.v + 1 >= el->el_terminal.t_size.v) {
1080                         int i, lins = el->el_terminal.t_size.v;
1081                         Char *firstline = el->el_display[0];
1082         
1083                         for(i = 1; i < lins; i++)
1084                                 el->el_display[i - 1] = el->el_display[i];
1085
1086                         re__copy_and_pad(firstline, STR(""), (size_t)0);
1087                         el->el_display[i - 1] = firstline;
1088                 } else {
1089                         el->el_cursor.v++;
1090                         el->el_refresh.r_oldcv++;
1091                 }
1092                 if (EL_HAS_AUTO_MARGINS) {
1093                         if (EL_HAS_MAGIC_MARGINS) {
1094                                 terminal__putc(el, ' ');
1095                                 terminal__putc(el, '\b');
1096                         }
1097                 } else {
1098                         terminal__putc(el, '\r');
1099                         terminal__putc(el, '\n');
1100                 }
1101         }
1102 }
1103
1104
1105 /* re_fastaddc():
1106  *      we added just one char, handle it fast.
1107  *      Assumes that screen cursor == real cursor
1108  */
1109 protected void
1110 re_fastaddc(EditLine *el)
1111 {
1112         Char c;
1113         int rhdiff;
1114
1115         c = el->el_line.cursor[-1];
1116
1117         if (c == '\t' || el->el_line.cursor != el->el_line.lastchar) {
1118                 re_refresh(el); /* too hard to handle */
1119                 return;
1120         }
1121         rhdiff = el->el_terminal.t_size.h - el->el_cursor.h -
1122             el->el_rprompt.p_pos.h;
1123         if (el->el_rprompt.p_pos.h && rhdiff < 3) {
1124                 re_refresh(el); /* clear out rprompt if less than 1 char gap */
1125                 return;
1126         }                       /* else (only do at end of line, no TAB) */
1127         switch (ct_chr_class(c)) {
1128         case CHTYPE_TAB: /* already handled, should never happen here */
1129                 break;
1130         case CHTYPE_NL:
1131         case CHTYPE_PRINT:
1132                 re_fastputc(el, c);
1133                 break;
1134         case CHTYPE_ASCIICTL:
1135         case CHTYPE_NONPRINT: {
1136                 Char visbuf[VISUAL_WIDTH_MAX];
1137                 ssize_t i, n =
1138                     ct_visual_char(visbuf, VISUAL_WIDTH_MAX, (Char)c);
1139                 for (i = 0; n-- > 0; ++i)
1140                         re_fastputc(el, visbuf[i]);
1141                 break;
1142         }
1143         }
1144         terminal__flush(el);
1145 }
1146
1147
1148 /* re_clear_display():
1149  *      clear the screen buffers so that new new prompt starts fresh.
1150  */
1151 protected void
1152 re_clear_display(EditLine *el)
1153 {
1154         int i;
1155
1156         el->el_cursor.v = 0;
1157         el->el_cursor.h = 0;
1158         for (i = 0; i < el->el_terminal.t_size.v; i++)
1159                 el->el_display[i][0] = '\0';
1160         el->el_refresh.r_oldcv = 0;
1161 }
1162
1163
1164 /* re_clear_lines():
1165  *      Make sure all lines are *really* blank
1166  */
1167 protected void
1168 re_clear_lines(EditLine *el)
1169 {
1170
1171         if (EL_CAN_CEOL) {
1172                 int i;
1173                 for (i = el->el_refresh.r_oldcv; i >= 0; i--) {
1174                         /* for each line on the screen */
1175                         terminal_move_to_line(el, i);
1176                         terminal_move_to_char(el, 0);
1177                         terminal_clear_EOL(el, el->el_terminal.t_size.h);
1178                 }
1179         } else {
1180                 terminal_move_to_line(el, el->el_refresh.r_oldcv);
1181                                         /* go to last line */
1182                 terminal__putc(el, '\r');       /* go to BOL */
1183                 terminal__putc(el, '\n');       /* go to new line */
1184         }
1185 }