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