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