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