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