refresh.c revision 1.12 1 /* $NetBSD: refresh.c,v 1.12 2000/01/20 22:56:21 christos 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.12 2000/01/20 22:56:21 christos 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 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) {
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 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