1 /* $NetBSD: refresh.c,v 1.34 2009/12/28 22:15:36 christos Exp $ */
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
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
36 #if !defined(lint) && !defined(SCCSID)
38 static char sccsid
[] = "@(#)refresh.c 8.1 (Berkeley) 6/4/93";
40 __RCSID("$NetBSD: refresh.c,v 1.34 2009/12/28 22:15:36 christos Exp $");
42 #endif /* not lint && not SCCSID */
45 * refresh.c: Lower level screen refreshing functions
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);
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) { \
72 while (/*CONSTCOND*/0)
73 #define ELRE_DEBUG(a, b) ELRE_ASSERT(a,b,;)
76 * Print a string on the debugging pty
79 re_printstr(EditLine
*el
, const char *str
, char *f
, char *t
)
82 ELRE_DEBUG(1, (__F
, "%s:\"", str
));
84 ELRE_DEBUG(1, (__F
, "%c", *f
++ & 0177));
85 ELRE_DEBUG(1, (__F
, "\"\r\n"));
88 #define ELRE_ASSERT(a, b, c)
89 #define ELRE_DEBUG(a, b)
93 * Move to the next line or scroll
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_term
.t_size
.v
) {
107 int i
, lins
= el
->el_term
.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
;
116 el
->el_refresh
.r_cursor
.v
++;
118 ELRE_ASSERT(el
->el_refresh
.r_cursor
.v
>= el
->el_term
.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_term
.t_size
.v
),
125 * Draw c, expanding tabs, control chars etc.
128 re_addc(EditLine
*el
, Int c
)
130 switch (ct_chr_class((Char
)c
)) {
131 case CHTYPE_TAB
: /* expand the tab */
134 if ((el
->el_refresh
.r_cursor
.h
& 07) == 0)
135 break; /* go until tab stop */
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 */
149 Char visbuf
[VISUAL_WIDTH_MAX
];
151 ct_visual_char(visbuf
, VISUAL_WIDTH_MAX
, (Char
)c
);
152 for (i
= 0; n
-- > 0; ++i
)
153 re_putc(el
, visbuf
[i
], 1);
161 * Draw the character given
164 re_putc(EditLine
*el
, Int c
, int shift
)
167 ELRE_DEBUG(1, (__F
, "printing %5x '%c'\r\n", c
, c
));
169 while (shift
&& (el
->el_refresh
.r_cursor
.h
+ w
> el
->el_term
.t_size
.h
))
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 */
177 el
->el_vdisplay
[el
->el_refresh
.r_cursor
.v
]
178 [el
->el_refresh
.r_cursor
.h
+ i
] = MB_FILL_CHAR
;
183 el
->el_refresh
.r_cursor
.h
+= w
; /* advance to next place */
184 if (el
->el_refresh
.r_cursor
.h
>= el
->el_term
.t_size
.h
) {
185 /* assure end of line */
186 el
->el_vdisplay
[el
->el_refresh
.r_cursor
.v
][el
->el_term
.t_size
.h
]
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.
200 re_refresh(EditLine
*el
)
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;
228 el
->el_line
.cursor
= el
->el_line
.lastchar
;
231 cur
.h
= -1; /* set flag in case I'm not set */
234 prompt_print(el
, EL_PROMPT
);
236 /* draw the current input buffer */
238 termsz
= el
->el_term
.t_size
.h
* el
->el_term
.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_term
.t_size
.v
) - 1)
248 * el
->el_term
.t_size
.v
));
251 st
= el
->el_line
.buffer
;
253 for (cp
= st
; cp
< el
->el_line
.lastchar
; cp
++) {
254 if (cp
== el
->el_line
.cursor
) {
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_term
.t_size
.h
) {
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_term
.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 */
284 prompt_print(el
, EL_RPROMPT
);
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
;
295 "term.h=%d vcur.h=%d vcur.v=%d vdisplay[0]=\r\n:%80.80s:\r\n",
296 el
->el_term
.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
311 re__copy_and_pad(el
->el_display
[i
], el
->el_vdisplay
[i
],
312 (size_t) el
->el_term
.t_size
.h
);
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 term_move_to_line(el
, i
);
321 term_move_to_char(el
, 0);
322 /* This Strlen should be safe even with MB_FILL_CHARs */
323 term_clear_EOL(el
, (int) Strlen(el
->el_display
[i
]));
325 term_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 */
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
,
335 term_move_to_line(el
, cur
.v
); /* go to where the cursor is */
336 term_move_to_char(el
, cur
.h
);
341 * used to go to last used screen line
344 re_goto_bottom(EditLine
*el
)
347 term_move_to_line(el
, el
->el_refresh
.r_oldcv
);
348 term__putc(el
, '\n');
349 re_clear_display(el
);
355 * insert num characters of s into d (in front of the character)
356 * at dat, maximum length of d is dlen
360 re_insert(EditLine
*el
__attribute__((__unused__
)),
361 Char
*d
, int dat
, int dlen
, Char
*s
, int num
)
367 if (num
> dlen
- dat
)
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 */
381 d
[dlen
] = '\0'; /* just in case */
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
--)
394 /* ct_encode_string() uses a static buffer, so we can't conveniently
395 * encode both d & s here */
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
));
405 * delete num characters d at dat, maximum length of d is dlen
409 re_delete(EditLine
*el
__attribute__((__unused__
)),
410 Char
*d
, int dat
, int dlen
, int num
)
416 if (dat
+ num
>= dlen
) {
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 */
430 d
[dlen
] = '\0'; /* just in case */
433 (__F
, "re_delete() after delete: %d at %d max %d, d == \"%s\"\n",
434 num
, dat
, dlen
, ct_encode_string(d
)));
439 * Like strncpy without padding.
442 re__strncopy(Char
*a
, Char
*b
, size_t n
)
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.
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",
472 ELRE_DEBUG(1, (__F
, "re_clear_eol %d\n", diff
));
473 term_clear_EOL(el
, diff
);
476 /*****************************************************************
477 re_update_line() is based on finding the middle difference of each line
480 /old first difference
481 /beginning of line | /old last same /old EOL
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
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
501 re_update_line(EditLine
*el
, Char
*old
, Char
*new, int i
)
504 Char
*ofd
, *ols
, *oe
, *nfd
, *nls
, *ne
;
505 Char
*osb
, *ose
, *nsb
, *nse
;
512 for (o
= old
, n
= new; *o
&& (*o
== *n
); o
++, n
++)
518 * Find the end of both old and new
523 * Remove any trailing blanks off of the end, being careful not to
524 * back up past the beginning.
537 /* remove blanks from end of new */
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"));
554 * find last same pointer
556 while ((o
> ofd
) && (n
> nfd
) && (*--o
== *--n
))
562 * find same begining and same end
570 * case 1: insert: scan from nfd to nls looking for *ofd
573 for (c
= *ofd
, n
= nfd
; n
< nls
; n
++) {
576 p
< nls
&& o
< ols
&& *o
== *p
;
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
)) {
594 * case 2: delete: scan from ofd to ols looking for *nfd
597 for (c
= *nfd
, o
= ofd
; o
< ols
; o
++) {
600 p
< ols
&& n
< nls
&& *p
== *n
;
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
)) {
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
) {
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
) {
652 if ((ols
- ofd
) < (nls
- nfd
)) {
657 if (!EL_CAN_DELETE
) {
668 if ((ols
- ofd
) > (nls
- nfd
)) {
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
) {
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));
697 "xxx-xxx:\"00000000001111111111222222222233333333334\"\r\n"));
699 "xxx-xxx:\"01234567890123456789012345678901234567890\"\r\n"));
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
720 term_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_term.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
759 * We need to do an insert!
760 * else if (we need to delete characters)
761 * We need to delete characters!
763 * No insert or delete
765 if ((nsb
!= nfd
) && fx
> 0 &&
766 ((p
- old
) + fx
<= el
->el_term
.t_size
.h
)) {
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 term_move_to_char(el
, (int)(nfd
- new));
774 * Check if we have stuff to keep at end
777 ELRE_DEBUG(1, (__F
, "with stuff to keep at end\r\n"));
779 * insert fx chars of new starting at nfd
782 ELRE_DEBUG(!EL_CAN_INSERT
, (__F
,
783 "ERROR: cannot insert in early first diff\n"));
784 term_insertwrite(el
, nfd
, fx
);
785 re_insert(el
, old
, (int)(ofd
- old
),
786 el
->el_term
.t_size
.h
, nfd
, fx
);
789 * write (nsb-nfd) - fx chars of new starting at
792 len
= (size_t) ((nsb
- nfd
) - fx
);
793 term_overwrite(el
, (nfd
+ fx
), len
);
794 re__strncopy(ofd
+ fx
, nfd
+ fx
, len
);
796 ELRE_DEBUG(1, (__F
, "without anything to save\r\n"));
797 len
= (size_t)(nsb
- nfd
);
798 term_overwrite(el
, nfd
, len
);
799 re__strncopy(ofd
, nfd
, len
);
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 term_move_to_char(el
, (int)(ofd
- old
));
813 * Check if we have stuff to save
816 ELRE_DEBUG(1, (__F
, "with stuff to save at end\r\n"));
818 * fx is less than zero *always* here but we check
822 ELRE_DEBUG(!EL_CAN_DELETE
, (__F
,
823 "ERROR: cannot delete in first diff\n"));
824 term_deletechars(el
, -fx
);
825 re_delete(el
, old
, (int)(ofd
- old
),
826 el
->el_term
.t_size
.h
, -fx
);
829 * write (nsb-nfd) chars of new starting at nfd
831 len
= (size_t) (nsb
- nfd
);
832 term_overwrite(el
, nfd
, len
);
833 re__strncopy(ofd
, nfd
, len
);
837 "but with nothing left to save\r\n"));
839 * write (nsb-nfd) chars of new starting at nfd
841 term_overwrite(el
, nfd
, (size_t)(nsb
- nfd
));
842 re_clear_eol(el
, fx
, sx
,
843 (int)((oe
- old
) - (ne
- new)));
852 if (sx
< 0 && (ose
- old
) + fx
< el
->el_term
.t_size
.h
) {
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 term_move_to_char(el
, (int)((ose
- old
) + fx
));
864 * Check if we have stuff to save
867 ELRE_DEBUG(1, (__F
, "with stuff to save at end\r\n"));
869 * Again a duplicate test.
872 ELRE_DEBUG(!EL_CAN_DELETE
, (__F
,
873 "ERROR: cannot delete in second diff\n"));
874 term_deletechars(el
, -sx
);
877 * write (nls-nse) chars of new starting at nse
879 term_overwrite(el
, nse
, (size_t)(nls
- nse
));
882 "but with nothing left to save\r\n"));
883 term_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",
895 term_move_to_char(el
, (int)(nfd
- new));
897 * Check if we have stuff to keep at the end
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
));
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 term_insertwrite(el
, nfd
, fx
);
914 re_insert(el
, old
, (int)(ofd
- old
),
915 el
->el_term
.t_size
.h
, nfd
, fx
);
918 * write (nsb-nfd) - fx chars of new starting at
921 len
= (size_t) ((nsb
- nfd
) - fx
);
922 term_overwrite(el
, (nfd
+ fx
), len
);
923 re__strncopy(ofd
+ fx
, nfd
+ fx
, len
);
925 ELRE_DEBUG(1, (__F
, "without anything to save\r\n"));
926 len
= (size_t) (nsb
- nfd
);
927 term_overwrite(el
, nfd
, len
);
928 re__strncopy(ofd
, nfd
, len
);
932 * line is now NEW up to nse
936 "second diff insert at %d...\r\n", (int)(nse
- new)));
937 term_move_to_char(el
, (int)(nse
- new));
939 ELRE_DEBUG(1, (__F
, "with stuff to keep at end\r\n"));
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 term_insertwrite(el
, nse
, sx
);
947 * write (nls-nse) - sx chars of new starting at
950 term_overwrite(el
, (nse
+ sx
),
951 (size_t)((nls
- nse
) - sx
));
953 ELRE_DEBUG(1, (__F
, "without anything to save\r\n"));
954 term_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
971 re__copy_and_pad(Char
*dst
, const Char
*src
, size_t width
)
975 for (i
= 0; i
< width
; i
++) {
981 for (; i
< width
; i
++)
988 /* re_refresh_cursor():
989 * Move to the new cursor position
992 re_refresh_cursor(EditLine
*el
)
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;
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_term
.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 */
1017 case CHTYPE_TAB
: /* if a tab, to next tab stop */
1023 if (w
> 1 && h
+ w
> th
) { /* won't fit on line */
1027 h
+= ct_visual_width(*cp
);
1031 if (h
>= th
) { /* check, extra long tabs picked up here also */
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)
1045 term_move_to_line(el
, v
);
1046 term_move_to_char(el
, h
);
1052 * Add a character fast.
1055 re_fastputc(EditLine
*el
, Int c
)
1057 int w
= Width((Char
)c
);
1058 while (w
> 1 && el
->el_cursor
.h
+ w
> el
->el_term
.t_size
.h
)
1059 re_fastputc(el
, ' ');
1062 el
->el_display
[el
->el_cursor
.v
][el
->el_cursor
.h
++] = c
;
1064 el
->el_display
[el
->el_cursor
.v
][el
->el_cursor
.h
++]
1067 if (el
->el_cursor
.h
>= el
->el_term
.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_term
.t_size
.v
) {
1078 int i
, lins
= el
->el_term
.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(""), 0);
1085 el
->el_display
[i
- 1] = firstline
;
1088 el
->el_refresh
.r_oldcv
++;
1090 if (EL_HAS_AUTO_MARGINS
) {
1091 if (EL_HAS_MAGIC_MARGINS
) {
1092 term__putc(el
, ' ');
1093 term__putc(el
, '\b');
1096 term__putc(el
, '\r');
1097 term__putc(el
, '\n');
1104 * we added just one char, handle it fast.
1105 * Assumes that screen cursor == real cursor
1108 re_fastaddc(EditLine
*el
)
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 */
1119 rhdiff
= el
->el_term
.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 */
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 */
1132 case CHTYPE_ASCIICTL
:
1133 case CHTYPE_NONPRINT
: {
1134 Char visbuf
[VISUAL_WIDTH_MAX
];
1136 ct_visual_char(visbuf
, VISUAL_WIDTH_MAX
, (Char
)c
);
1137 for (i
= 0; n
-- > 0; ++i
)
1138 re_fastputc(el
, visbuf
[i
]);
1146 /* re_clear_display():
1147 * clear the screen buffers so that new new prompt starts fresh.
1150 re_clear_display(EditLine
*el
)
1154 el
->el_cursor
.v
= 0;
1155 el
->el_cursor
.h
= 0;
1156 for (i
= 0; i
< el
->el_term
.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
1166 re_clear_lines(EditLine
*el
)
1171 for (i
= el
->el_refresh
.r_oldcv
; i
>= 0; i
--) {
1172 /* for each line on the screen */
1173 term_move_to_line(el
, i
);
1174 term_move_to_char(el
, 0);
1175 term_clear_EOL(el
, el
->el_term
.t_size
.h
);
1178 term_move_to_line(el
, el
->el_refresh
.r_oldcv
);
1179 /* go to last line */
1180 term__putc(el
, '\r'); /* go to BOL */
1181 term__putc(el
, '\n'); /* go to new line */