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