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