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