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