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