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