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