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