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