More minor IPI work.
[dragonfly/vkernel-mp.git] / lib / libedit / refresh.c
blob93a984a581222682263994c2a1ee5351ec2a2296
1 /*-
2 * Copyright (c) 1992, 1993
3 * The Regents of the University of California. All rights reserved.
5 * This code is derived from software contributed to Berkeley by
6 * Christos Zoulas of Cornell University.
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.
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.
32 * @(#)refresh.c 8.1 (Berkeley) 6/4/93
33 * $NetBSD: refresh.c,v 1.27 2005/11/09 22:11:10 christos Exp $
34 * $DragonFly: src/lib/libedit/refresh.c,v 1.6 2007/05/05 00:27:39 pavalos Exp $
37 #include "config.h"
40 * refresh.c: Lower level screen refreshing functions
42 #include <stdio.h>
43 #include <ctype.h>
44 #include <unistd.h>
45 #include <string.h>
47 #include "el.h"
49 private void re_addc(EditLine *, int);
50 private void re_update_line(EditLine *, char *, char *, int);
51 private void re_insert (EditLine *, char *, int, int, char *, int);
52 private void re_delete(EditLine *, char *, int, int, int);
53 private void re_fastputc(EditLine *, int);
54 private void re_clear_eol(EditLine *, int, int, int);
55 private void re__strncopy(char *, char *, size_t);
56 private void re__copy_and_pad(char *, const char *, size_t);
58 #ifdef DEBUG_REFRESH
59 private void re_printstr(EditLine *, const char *, char *, char *);
60 #define __F el->el_errfile
61 #define ELRE_ASSERT(a, b, c) do \
62 if (/*CONSTCOND*/ a) { \
63 (void) fprintf b; \
64 c; \
65 } \
66 while (/*CONSTCOND*/0)
67 #define ELRE_DEBUG(a, b) ELRE_ASSERT(a,b,;)
69 /* re_printstr():
70 * Print a string on the debugging pty
72 private void
73 re_printstr(EditLine *el, const char *str, char *f, char *t)
76 ELRE_DEBUG(1, (__F, "%s:\"", str));
77 while (f < t)
78 ELRE_DEBUG(1, (__F, "%c", *f++ & 0177));
79 ELRE_DEBUG(1, (__F, "\"\r\n"));
81 #else
82 #define ELRE_ASSERT(a, b, c)
83 #define ELRE_DEBUG(a, b)
84 #endif
87 /* re_addc():
88 * Draw c, expanding tabs, control chars etc.
90 private void
91 re_addc(EditLine *el, int c)
94 if (isprint(c)) {
95 re_putc(el, c, 1);
96 return;
98 if (c == '\n') { /* expand the newline */
99 int oldv = el->el_refresh.r_cursor.v;
100 re_putc(el, '\0', 0); /* assure end of line */
101 if (oldv == el->el_refresh.r_cursor.v) { /* XXX */
102 el->el_refresh.r_cursor.h = 0; /* reset cursor pos */
103 el->el_refresh.r_cursor.v++;
105 return;
107 if (c == '\t') { /* expand the tab */
108 for (;;) {
109 re_putc(el, ' ', 1);
110 if ((el->el_refresh.r_cursor.h & 07) == 0)
111 break; /* go until tab stop */
113 } else if (iscntrl(c)) {
114 re_putc(el, '^', 1);
115 if (c == '\177')
116 re_putc(el, '?', 1);
117 else
118 /* uncontrolify it; works only for iso8859-1 like sets */
119 re_putc(el, (c | 0100), 1);
120 } else {
121 re_putc(el, '\\', 1);
122 re_putc(el, (int) ((((unsigned int) c >> 6) & 07) + '0'), 1);
123 re_putc(el, (int) ((((unsigned int) c >> 3) & 07) + '0'), 1);
124 re_putc(el, (c & 07) + '0', 1);
129 /* re_putc():
130 * Draw the character given
132 protected void
133 re_putc(EditLine *el, int c, int shift)
136 ELRE_DEBUG(1, (__F, "printing %3.3o '%c'\r\n", c, c));
138 el->el_vdisplay[el->el_refresh.r_cursor.v][el->el_refresh.r_cursor.h] = c;
139 if (!shift)
140 return;
142 el->el_refresh.r_cursor.h++; /* advance to next place */
143 if (el->el_refresh.r_cursor.h >= el->el_term.t_size.h) {
144 el->el_vdisplay[el->el_refresh.r_cursor.v][el->el_term.t_size.h] = '\0';
145 /* assure end of line */
146 el->el_refresh.r_cursor.h = 0; /* reset it. */
149 * If we would overflow (input is longer than terminal size),
150 * emulate scroll by dropping first line and shuffling the rest.
151 * We do this via pointer shuffling - it's safe in this case
152 * and we avoid memcpy().
154 if (el->el_refresh.r_cursor.v + 1 >= el->el_term.t_size.v) {
155 int i, lins = el->el_term.t_size.v;
156 char *firstline = el->el_vdisplay[0];
158 for(i=1; i < lins; i++)
159 el->el_vdisplay[i-1] = el->el_vdisplay[i];
161 firstline[0] = '\0'; /* empty the string */
162 el->el_vdisplay[i-1] = firstline;
163 } else
164 el->el_refresh.r_cursor.v++;
166 ELRE_ASSERT(el->el_refresh.r_cursor.v >= el->el_term.t_size.v,
167 (__F, "\r\nre_putc: overflow! r_cursor.v == %d > %d\r\n",
168 el->el_refresh.r_cursor.v, el->el_term.t_size.v),
169 abort());
174 /* re_refresh():
175 * draws the new virtual screen image from the current input
176 * line, then goes line-by-line changing the real image to the new
177 * virtual image. The routine to re-draw a line can be replaced
178 * easily in hopes of a smarter one being placed there.
180 protected void
181 re_refresh(EditLine *el)
183 int i, rhdiff;
184 char *cp, *st;
185 coord_t cur;
186 #ifdef notyet
187 size_t termsz;
188 #endif
190 ELRE_DEBUG(1, (__F, "el->el_line.buffer = :%s:\r\n",
191 el->el_line.buffer));
193 /* reset the Drawing cursor */
194 el->el_refresh.r_cursor.h = 0;
195 el->el_refresh.r_cursor.v = 0;
197 /* temporarily draw rprompt to calculate its size */
198 prompt_print(el, EL_RPROMPT);
200 /* reset the Drawing cursor */
201 el->el_refresh.r_cursor.h = 0;
202 el->el_refresh.r_cursor.v = 0;
204 if (el->el_line.cursor >= el->el_line.lastchar) {
205 if (el->el_map.current == el->el_map.alt
206 && el->el_line.lastchar != el->el_line.buffer)
207 el->el_line.cursor = el->el_line.lastchar - 1;
208 else
209 el->el_line.cursor = el->el_line.lastchar;
212 cur.h = -1; /* set flag in case I'm not set */
213 cur.v = 0;
215 prompt_print(el, EL_PROMPT);
217 /* draw the current input buffer */
218 #if 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) {
222 * If line is longer than terminal, process only part
223 * of line which would influence display.
225 size_t rem = (el->el_line.lastchar-el->el_line.buffer)%termsz;
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;
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;
240 re_addc(el, (unsigned char) *cp);
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;
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) {
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.
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;
264 re_putc(el, '\0', 0); /* make line ended with NUL, no cursor shift */
266 el->el_refresh.r_newcv = el->el_refresh.r_cursor.v;
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]));
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);
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.
285 re__copy_and_pad(el->el_display[i], el->el_vdisplay[i],
286 (size_t) el->el_term.t_size.h);
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));
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';
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);
313 /* re_goto_bottom():
314 * used to go to last used screen line
316 protected void
317 re_goto_bottom(EditLine *el)
320 term_move_to_line(el, el->el_refresh.r_oldcv);
321 term__putc('\n');
322 re_clear_display(el);
323 term__flush();
327 /* re_insert():
328 * insert num characters of s into d (in front of the character)
329 * at dat, maximum length of d is dlen
331 private void
332 /*ARGSUSED*/
333 re_insert(EditLine *el __attribute__((__unused__)),
334 char *d, int dat, int dlen, char *s, int num)
336 char *a, *b;
338 if (num <= 0)
339 return;
340 if (num > dlen - dat)
341 num = dlen - dat;
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));
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 */
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));
361 /* copy the characters */
362 for (a = d + dat; (a < d + dlen) && (num > 0); num--)
363 *a++ = *s++;
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));
372 /* re_delete():
373 * delete num characters d at dat, maximum length of d is dlen
375 private void
376 /*ARGSUSED*/
377 re_delete(EditLine *el __attribute__((__unused__)),
378 char *d, int dat, int dlen, int num)
380 char *a, *b;
382 if (num <= 0)
383 return;
384 if (dat + num >= dlen) {
385 d[dat] = '\0';
386 return;
388 ELRE_DEBUG(1,
389 (__F, "re_delete() starting: %d at %d max %d, d == \"%s\"\n",
390 num, dat, dlen, d));
392 /* open up the space for num chars */
393 if (num > 0) {
394 b = d + dat;
395 a = b + num;
396 while (a < &d[dlen])
397 *b++ = *a++;
398 d[dlen] = '\0'; /* just in case */
400 ELRE_DEBUG(1,
401 (__F, "re_delete() after delete: %d at %d max %d, d == \"%s\"\n",
402 num, dat, dlen, d));
406 /* re__strncopy():
407 * Like strncpy without padding.
409 private void
410 re__strncopy(char *a, char *b, size_t n)
413 while (n-- && *b)
414 *a++ = *b++;
417 /* re_clear_eol():
418 * Find the number of characters we need to clear till the end of line
419 * in order to make sure that we have cleared the previous contents of
420 * the line. fx and sx is the number of characters inserted or deleted
421 * int the first or second diff, diff is the difference between the
422 * number of characters between the new and old line.
424 private void
425 re_clear_eol(EditLine *el, int fx, int sx, int diff)
428 ELRE_DEBUG(1, (__F, "re_clear_eol sx %d, fx %d, diff %d\n",
429 sx, fx, diff));
431 if (fx < 0)
432 fx = -fx;
433 if (sx < 0)
434 sx = -sx;
435 if (fx > diff)
436 diff = fx;
437 if (sx > diff)
438 diff = sx;
440 ELRE_DEBUG(1, (__F, "re_clear_eol %d\n", diff));
441 term_clear_EOL(el, diff);
444 /*****************************************************************
445 re_update_line() is based on finding the middle difference of each line
446 on the screen; vis:
448 /old first difference
449 /beginning of line | /old last same /old EOL
450 v v v v
451 old: eddie> Oh, my little gruntle-buggy is to me, as lurgid as
452 new: eddie> Oh, my little buggy says to me, as lurgid as
453 ^ ^ ^ ^
454 \beginning of line | \new last same \new end of line
455 \new first difference
457 all are character pointers for the sake of speed. Special cases for
458 no differences, as well as for end of line additions must be handled.
459 **************************************************************** */
461 /* Minimum at which doing an insert it "worth it". This should be about
462 * half the "cost" of going into insert mode, inserting a character, and
463 * going back out. This should really be calculated from the termcap
464 * data... For the moment, a good number for ANSI terminals.
466 #define MIN_END_KEEP 4
468 private void
469 re_update_line(EditLine *el, char *old, char *new, int i)
471 char *o, *n, *p, c;
472 char *ofd, *ols, *oe, *nfd, *nls, *ne;
473 char *osb, *ose, *nsb, *nse;
474 int fx, sx;
477 * find first diff
479 for (o = old, n = new; *o && (*o == *n); o++, n++)
480 continue;
481 ofd = o;
482 nfd = n;
485 * Find the end of both old and new
487 while (*o)
488 o++;
490 * Remove any trailing blanks off of the end, being careful not to
491 * back up past the beginning.
493 while (ofd < o) {
494 if (o[-1] != ' ')
495 break;
496 o--;
498 oe = o;
499 *oe = '\0';
501 while (*n)
502 n++;
504 /* remove blanks from end of new */
505 while (nfd < n) {
506 if (n[-1] != ' ')
507 break;
508 n--;
510 ne = n;
511 *ne = '\0';
514 * if no diff, continue to next line of redraw
516 if (*ofd == '\0' && *nfd == '\0') {
517 ELRE_DEBUG(1, (__F, "no difference.\r\n"));
518 return;
521 * find last same pointer
523 while ((o > ofd) && (n > nfd) && (*--o == *--n))
524 continue;
525 ols = ++o;
526 nls = ++n;
529 * find same begining and same end
531 osb = ols;
532 nsb = nls;
533 ose = ols;
534 nse = nls;
537 * case 1: insert: scan from nfd to nls looking for *ofd
539 if (*ofd) {
540 for (c = *ofd, n = nfd; n < nls; n++) {
541 if (c == *n) {
542 for (o = ofd, p = n;
543 p < nls && o < ols && *o == *p;
544 o++, p++)
545 continue;
547 * if the new match is longer and it's worth
548 * keeping, then we take it
550 if (((nse - nsb) < (p - n)) &&
551 (2 * (p - n) > n - nfd)) {
552 nsb = n;
553 nse = p;
554 osb = ofd;
555 ose = o;
561 * case 2: delete: scan from ofd to ols looking for *nfd
563 if (*nfd) {
564 for (c = *nfd, o = ofd; o < ols; o++) {
565 if (c == *o) {
566 for (n = nfd, p = o;
567 p < ols && n < nls && *p == *n;
568 p++, n++)
569 continue;
571 * if the new match is longer and it's worth
572 * keeping, then we take it
574 if (((ose - osb) < (p - o)) &&
575 (2 * (p - o) > o - ofd)) {
576 nsb = nfd;
577 nse = n;
578 osb = o;
579 ose = p;
585 * Pragmatics I: If old trailing whitespace or not enough characters to
586 * save to be worth it, then don't save the last same info.
588 if ((oe - ols) < MIN_END_KEEP) {
589 ols = oe;
590 nls = ne;
593 * Pragmatics II: if the terminal isn't smart enough, make the data
594 * dumber so the smart update doesn't try anything fancy
598 * fx is the number of characters we need to insert/delete: in the
599 * beginning to bring the two same begins together
601 fx = (nsb - nfd) - (osb - ofd);
603 * sx is the number of characters we need to insert/delete: in the
604 * end to bring the two same last parts together
606 sx = (nls - nse) - (ols - ose);
608 if (!EL_CAN_INSERT) {
609 if (fx > 0) {
610 osb = ols;
611 ose = ols;
612 nsb = nls;
613 nse = nls;
615 if (sx > 0) {
616 ols = oe;
617 nls = ne;
619 if ((ols - ofd) < (nls - nfd)) {
620 ols = oe;
621 nls = ne;
624 if (!EL_CAN_DELETE) {
625 if (fx < 0) {
626 osb = ols;
627 ose = ols;
628 nsb = nls;
629 nse = nls;
631 if (sx < 0) {
632 ols = oe;
633 nls = ne;
635 if ((ols - ofd) > (nls - nfd)) {
636 ols = oe;
637 nls = ne;
641 * Pragmatics III: make sure the middle shifted pointers are correct if
642 * they don't point to anything (we may have moved ols or nls).
644 /* if the change isn't worth it, don't bother */
645 /* was: if (osb == ose) */
646 if ((ose - osb) < MIN_END_KEEP) {
647 osb = ols;
648 ose = ols;
649 nsb = nls;
650 nse = nls;
653 * Now that we are done with pragmatics we recompute fx, sx
655 fx = (nsb - nfd) - (osb - ofd);
656 sx = (nls - nse) - (ols - ose);
658 ELRE_DEBUG(1, (__F, "fx %d, sx %d\n", fx, sx));
659 ELRE_DEBUG(1, (__F, "ofd %d, osb %d, ose %d, ols %d, oe %d\n",
660 ofd - old, osb - old, ose - old, ols - old, oe - old));
661 ELRE_DEBUG(1, (__F, "nfd %d, nsb %d, nse %d, nls %d, ne %d\n",
662 nfd - new, nsb - new, nse - new, nls - new, ne - new));
663 ELRE_DEBUG(1, (__F,
664 "xxx-xxx:\"00000000001111111111222222222233333333334\"\r\n"));
665 ELRE_DEBUG(1, (__F,
666 "xxx-xxx:\"01234567890123456789012345678901234567890\"\r\n"));
667 #ifdef DEBUG_REFRESH
668 re_printstr(el, "old- oe", old, oe);
669 re_printstr(el, "new- ne", new, ne);
670 re_printstr(el, "old-ofd", old, ofd);
671 re_printstr(el, "new-nfd", new, nfd);
672 re_printstr(el, "ofd-osb", ofd, osb);
673 re_printstr(el, "nfd-nsb", nfd, nsb);
674 re_printstr(el, "osb-ose", osb, ose);
675 re_printstr(el, "nsb-nse", nsb, nse);
676 re_printstr(el, "ose-ols", ose, ols);
677 re_printstr(el, "nse-nls", nse, nls);
678 re_printstr(el, "ols- oe", ols, oe);
679 re_printstr(el, "nls- ne", nls, ne);
680 #endif /* DEBUG_REFRESH */
683 * el_cursor.v to this line i MUST be in this routine so that if we
684 * don't have to change the line, we don't move to it. el_cursor.h to
685 * first diff char
687 term_move_to_line(el, i);
690 * at this point we have something like this:
692 * /old /ofd /osb /ose /ols /oe
693 * v.....................v v..................v v........v
694 * eddie> Oh, my fredded gruntle-buggy is to me, as foo var lurgid as
695 * eddie> Oh, my fredded quiux buggy is to me, as gruntle-lurgid as
696 * ^.....................^ ^..................^ ^........^
697 * \new \nfd \nsb \nse \nls \ne
699 * fx is the difference in length between the chars between nfd and
700 * nsb, and the chars between ofd and osb, and is thus the number of
701 * characters to delete if < 0 (new is shorter than old, as above),
702 * or insert (new is longer than short).
704 * sx is the same for the second differences.
708 * if we have a net insert on the first difference, AND inserting the
709 * net amount ((nsb-nfd) - (osb-ofd)) won't push the last useful
710 * character (which is ne if nls != ne, otherwise is nse) off the edge
711 * of the screen (el->el_term.t_size.h) else we do the deletes first
712 * so that we keep everything we need to.
716 * if the last same is the same like the end, there is no last same
717 * part, otherwise we want to keep the last same part set p to the
718 * last useful old character
720 p = (ols != oe) ? oe : ose;
723 * if (There is a diffence in the beginning) && (we need to insert
724 * characters) && (the number of characters to insert is less than
725 * the term width)
726 * We need to do an insert!
727 * else if (we need to delete characters)
728 * We need to delete characters!
729 * else
730 * No insert or delete
732 if ((nsb != nfd) && fx > 0 &&
733 ((p - old) + fx <= el->el_term.t_size.h)) {
734 ELRE_DEBUG(1,
735 (__F, "first diff insert at %d...\r\n", nfd - new));
737 * Move to the first char to insert, where the first diff is.
739 term_move_to_char(el, nfd - new);
741 * Check if we have stuff to keep at end
743 if (nsb != ne) {
744 ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
746 * insert fx chars of new starting at nfd
748 if (fx > 0) {
749 ELRE_DEBUG(!EL_CAN_INSERT, (__F,
750 "ERROR: cannot insert in early first diff\n"));
751 term_insertwrite(el, nfd, fx);
752 re_insert(el, old, ofd - old,
753 el->el_term.t_size.h, nfd, fx);
756 * write (nsb-nfd) - fx chars of new starting at
757 * (nfd + fx)
759 term_overwrite(el, nfd + fx, (nsb - nfd) - fx);
760 re__strncopy(ofd + fx, nfd + fx,
761 (size_t) ((nsb - nfd) - fx));
762 } else {
763 ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
764 term_overwrite(el, nfd, (nsb - nfd));
765 re__strncopy(ofd, nfd, (size_t) (nsb - nfd));
767 * Done
769 return;
771 } else if (fx < 0) {
772 ELRE_DEBUG(1,
773 (__F, "first diff delete at %d...\r\n", ofd - old));
775 * move to the first char to delete where the first diff is
777 term_move_to_char(el, ofd - old);
779 * Check if we have stuff to save
781 if (osb != oe) {
782 ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n"));
784 * fx is less than zero *always* here but we check
785 * for code symmetry
787 if (fx < 0) {
788 ELRE_DEBUG(!EL_CAN_DELETE, (__F,
789 "ERROR: cannot delete in first diff\n"));
790 term_deletechars(el, -fx);
791 re_delete(el, old, ofd - old,
792 el->el_term.t_size.h, -fx);
795 * write (nsb-nfd) chars of new starting at nfd
797 term_overwrite(el, nfd, (nsb - nfd));
798 re__strncopy(ofd, nfd, (size_t) (nsb - nfd));
800 } else {
801 ELRE_DEBUG(1, (__F,
802 "but with nothing left to save\r\n"));
804 * write (nsb-nfd) chars of new starting at nfd
806 term_overwrite(el, nfd, (nsb - nfd));
807 re_clear_eol(el, fx, sx, (oe - old) - (ne - new));
809 * Done
811 return;
813 } else
814 fx = 0;
816 if (sx < 0 && (ose - old) + fx < el->el_term.t_size.h) {
817 ELRE_DEBUG(1, (__F,
818 "second diff delete at %d...\r\n", (ose - old) + fx));
820 * Check if we have stuff to delete
823 * fx is the number of characters inserted (+) or deleted (-)
826 term_move_to_char(el, (ose - old) + fx);
828 * Check if we have stuff to save
830 if (ols != oe) {
831 ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n"));
833 * Again a duplicate test.
835 if (sx < 0) {
836 ELRE_DEBUG(!EL_CAN_DELETE, (__F,
837 "ERROR: cannot delete in second diff\n"));
838 term_deletechars(el, -sx);
841 * write (nls-nse) chars of new starting at nse
843 term_overwrite(el, nse, (nls - nse));
844 } else {
845 ELRE_DEBUG(1, (__F,
846 "but with nothing left to save\r\n"));
847 term_overwrite(el, nse, (nls - nse));
848 re_clear_eol(el, fx, sx, (oe - old) - (ne - new));
852 * if we have a first insert AND WE HAVEN'T ALREADY DONE IT...
854 if ((nsb != nfd) && (osb - ofd) <= (nsb - nfd) && (fx == 0)) {
855 ELRE_DEBUG(1, (__F, "late first diff insert at %d...\r\n",
856 nfd - new));
858 term_move_to_char(el, nfd - new);
860 * Check if we have stuff to keep at the end
862 if (nsb != ne) {
863 ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
865 * We have to recalculate fx here because we set it
866 * to zero above as a flag saying that we hadn't done
867 * an early first insert.
869 fx = (nsb - nfd) - (osb - ofd);
870 if (fx > 0) {
872 * insert fx chars of new starting at nfd
874 ELRE_DEBUG(!EL_CAN_INSERT, (__F,
875 "ERROR: cannot insert in late first diff\n"));
876 term_insertwrite(el, nfd, fx);
877 re_insert(el, old, ofd - old,
878 el->el_term.t_size.h, nfd, fx);
881 * write (nsb-nfd) - fx chars of new starting at
882 * (nfd + fx)
884 term_overwrite(el, nfd + fx, (nsb - nfd) - fx);
885 re__strncopy(ofd + fx, nfd + fx,
886 (size_t) ((nsb - nfd) - fx));
887 } else {
888 ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
889 term_overwrite(el, nfd, (nsb - nfd));
890 re__strncopy(ofd, nfd, (size_t) (nsb - nfd));
894 * line is now NEW up to nse
896 if (sx >= 0) {
897 ELRE_DEBUG(1, (__F,
898 "second diff insert at %d...\r\n", nse - new));
899 term_move_to_char(el, nse - new);
900 if (ols != oe) {
901 ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
902 if (sx > 0) {
903 /* insert sx chars of new starting at nse */
904 ELRE_DEBUG(!EL_CAN_INSERT, (__F,
905 "ERROR: cannot insert in second diff\n"));
906 term_insertwrite(el, nse, sx);
909 * write (nls-nse) - sx chars of new starting at
910 * (nse + sx)
912 term_overwrite(el, nse + sx, (nls - nse) - sx);
913 } else {
914 ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
915 term_overwrite(el, nse, (nls - nse));
918 * No need to do a clear-to-end here because we were
919 * doing a second insert, so we will have over
920 * written all of the old string.
924 ELRE_DEBUG(1, (__F, "done.\r\n"));
928 /* re__copy_and_pad():
929 * Copy string and pad with spaces
931 private void
932 re__copy_and_pad(char *dst, const char *src, size_t width)
934 size_t i;
936 for (i = 0; i < width; i++) {
937 if (*src == '\0')
938 break;
939 *dst++ = *src++;
942 for (; i < width; i++)
943 *dst++ = ' ';
945 *dst = '\0';
949 /* re_refresh_cursor():
950 * Move to the new cursor position
952 protected void
953 re_refresh_cursor(EditLine *el)
955 char *cp, c;
956 int h, v, th;
958 if (el->el_line.cursor >= el->el_line.lastchar) {
959 if (el->el_map.current == el->el_map.alt
960 && el->el_line.lastchar != el->el_line.buffer)
961 el->el_line.cursor = el->el_line.lastchar - 1;
962 else
963 el->el_line.cursor = el->el_line.lastchar;
966 /* first we must find where the cursor is... */
967 h = el->el_prompt.p_pos.h;
968 v = el->el_prompt.p_pos.v;
969 th = el->el_term.t_size.h; /* optimize for speed */
971 /* do input buffer to el->el_line.cursor */
972 for (cp = el->el_line.buffer; cp < el->el_line.cursor; cp++) {
973 c = *cp;
974 h++; /* all chars at least this long */
976 if (c == '\n') {/* handle newline in data part too */
977 h = 0;
978 v++;
979 } else {
980 if (c == '\t') { /* if a tab, to next tab stop */
981 while (h & 07) {
982 h++;
984 } else if (iscntrl((unsigned char) c)) {
985 /* if control char */
986 h++;
987 if (h > th) { /* if overflow, compensate */
988 h = 1;
989 v++;
991 } else if (!isprint((unsigned char) c)) {
992 h += 3;
993 if (h > th) { /* if overflow, compensate */
994 h = h - th;
995 v++;
1000 if (h >= th) { /* check, extra long tabs picked up here also */
1001 h = 0;
1002 v++;
1006 /* now go there */
1007 term_move_to_line(el, v);
1008 term_move_to_char(el, h);
1009 term__flush();
1013 /* re_fastputc():
1014 * Add a character fast.
1016 private void
1017 re_fastputc(EditLine *el, int c)
1020 term__putc(c);
1021 el->el_display[el->el_cursor.v][el->el_cursor.h++] = c;
1022 if (el->el_cursor.h >= el->el_term.t_size.h) {
1023 /* if we must overflow */
1024 el->el_cursor.h = 0;
1027 * If we would overflow (input is longer than terminal size),
1028 * emulate scroll by dropping first line and shuffling the rest.
1029 * We do this via pointer shuffling - it's safe in this case
1030 * and we avoid memcpy().
1032 if (el->el_cursor.v + 1 >= el->el_term.t_size.v) {
1033 int i, lins = el->el_term.t_size.v;
1034 char *firstline = el->el_display[0];
1036 for(i=1; i < lins; i++)
1037 el->el_display[i-1] = el->el_display[i];
1039 re__copy_and_pad(firstline, "", 0);
1040 el->el_display[i-1] = firstline;
1041 } else {
1042 el->el_cursor.v++;
1043 el->el_refresh.r_oldcv++;
1045 if (EL_HAS_AUTO_MARGINS) {
1046 if (EL_HAS_MAGIC_MARGINS) {
1047 term__putc(' ');
1048 term__putc('\b');
1050 } else {
1051 term__putc('\r');
1052 term__putc('\n');
1058 /* re_fastaddc():
1059 * we added just one char, handle it fast.
1060 * Assumes that screen cursor == real cursor
1062 protected void
1063 re_fastaddc(EditLine *el)
1065 char c;
1066 int rhdiff;
1068 c = el->el_line.cursor[-1];
1070 if (c == '\t' || el->el_line.cursor != el->el_line.lastchar) {
1071 re_refresh(el); /* too hard to handle */
1072 return;
1074 rhdiff = el->el_term.t_size.h - el->el_cursor.h -
1075 el->el_rprompt.p_pos.h;
1076 if (el->el_rprompt.p_pos.h && rhdiff < 3) {
1077 re_refresh(el); /* clear out rprompt if less than 1 char gap */
1078 return;
1079 } /* else (only do at end of line, no TAB) */
1080 if (iscntrl((unsigned char) c)) { /* if control char, do caret */
1081 char mc = (c == '\177') ? '?' : (c | 0100);
1082 re_fastputc(el, '^');
1083 re_fastputc(el, mc);
1084 } else if (isprint((unsigned char) c)) { /* normal char */
1085 re_fastputc(el, c);
1086 } else {
1087 re_fastputc(el, '\\');
1088 re_fastputc(el, (int)(((((unsigned int)c) >> 6) & 3) + '0'));
1089 re_fastputc(el, (int)(((((unsigned int)c) >> 3) & 7) + '0'));
1090 re_fastputc(el, (c & 7) + '0');
1092 term__flush();
1096 /* re_clear_display():
1097 * clear the screen buffers so that new new prompt starts fresh.
1099 protected void
1100 re_clear_display(EditLine *el)
1102 int i;
1104 el->el_cursor.v = 0;
1105 el->el_cursor.h = 0;
1106 for (i = 0; i < el->el_term.t_size.v; i++)
1107 el->el_display[i][0] = '\0';
1108 el->el_refresh.r_oldcv = 0;
1112 /* re_clear_lines():
1113 * Make sure all lines are *really* blank
1115 protected void
1116 re_clear_lines(EditLine *el)
1119 if (EL_CAN_CEOL) {
1120 int i;
1121 term_move_to_char(el, 0);
1122 for (i = 0; i <= el->el_refresh.r_oldcv; i++) {
1123 /* for each line on the screen */
1124 term_move_to_line(el, i);
1125 term_clear_EOL(el, el->el_term.t_size.h);
1127 term_move_to_line(el, 0);
1128 } else {
1129 term_move_to_line(el, el->el_refresh.r_oldcv);
1130 /* go to last line */
1131 term__putc('\r'); /* go to BOL */
1132 term__putc('\n'); /* go to new line */